Thursday, March 6, 2025

On Making Databases Run Faster

Database  technology is a mature field, and...

On Making Databases Run Faster

Database  technology is a mature field, and...

Case Study: Stefan Vitasović Portfolio — 2025

Web DevelopmentCase Study: Stefan Vitasović Portfolio — 2025


Hi! I’m Stefan, a creative developer based out of Stockholm, Sweden. In this case study, I’ll take you through the design and development process of my 2025 portfolio.

I love doing side projects as a creative outlet and as a place for experiments. As I realized I was behind with my own portfolio and wanted to use the opportunity to combine my love for typography and coding.

Aimed at showcasing my work focused on motion and interactivity, the site is based around a minimalist design complemented by dynamic visuals. These include typographic animations, WebGL video grid and seamless page transitions. The goal was to create an engaging and performance-optimized experience that communicates my work in a clean, bold, and creative fashion showcasing a selected group of projects I worked on over the past 10 years.

Design and motion

The design language is raw and unpolished, reflecting my passion for experimenting and creativity in the digital space. This aesthetic, inspired by Swiss print design, blends offset grid layouts with generous empty space and a strong focus on typography. It creates an atmosphere that calls for creative motion ideas.

Moodboard snippet

Here are a few of the design highlights:

  • Offset layouts: The page layouts are asymmetric and unbalanced in parts which adds dynamics to the visual presentation.
  • Geometry and typography: Angular and geometric shapes alongside typographic elements act as pivot points during page transitions and content loading. 
  • Interactive elements: Bold animations, glitchy shader effects, noisy lo-fi background and infinite scroll interactions all form an interplay between the rigid structured core and the more fluid digital overlay.
  • Motion motifs: I like to add motion motifs and patterns in the projects I work on. These can entail a lot of different things – from distinct micro-interactions to unusual page transitions and bold hero animations. Similar to a catchy part of a song, these visual hooks are repeating interactions or animations that catch the user’s eye and hopefully make the experience more memorable.

Tech stack

The portfolio was built using modern web technologies that ensure both high performance and leave room for creativity. Here’s a quick breakdown of the tools I used:

  • React: The core of the site is Next.js with its pages router (necessary for seamless page transitions) utilizing dynamic imports and code-splitting, server-side rendering, and static site generation.
  • Framer Motion: Framer Motion (now Motion) was the obvious choice for its ease of integration with React given the declarative nature and composability. To add to that, using the existing imperative functions like animate to manipulate shader uniforms is always handy.
  • SCSS modules: This provides scoped styles allowing for more maintainable and modular CSS.
  • Three.js & React Three Fiber: Interactive WebGL integration used for visual elements and shader effects, adding an additional layer of depth and motion to the site.
  • CDN: Video content is hosted on Cloudflare’s R2 bucket and delivered via its CDN.
  • Vercel: Hosting on Vercel provided seamless deployment, high availability, and performance optimizations.

Key features and highlights

Typography animation

The principal motion motif present on the site is the characters-to-word animation. This came about as an experiment while I was trying to code up something engaging that connects the raw typographic layouts with the digital style of interactions. The words are divided into individual characters which then come together through motion, illustrating how coding bits and pieces ultimately form the end product.

From a technical standpoint, this is achieved by splitting each string into a number of iterations and offsetting each segment based on its index and string length. In addition to that, limiting the segment area creates a mask and allows for a fluid assembly of the final word inside. The bits assemble together by having masked elements move along the x axis forming the glass-like parallax effect.

<span
  aria-hidden
  style={{
    left: `${(index / total) * 100}%`,
    width: `${100 / total + 0.01}%`,
  }}
>
  <motion.span
    initial={{ x: from }}
    animate={animate && { x: to }}
    transition={{
     duration: 1.25 + index * 0.025,
     delay: delay + index * 0.025,
     ease: easeExpOut,
    }}
  >
    {text}
  </motion.span>
</span>

Intro animation and page transitions

One of the core features of this portfolio are the page hero effects. Leveraging the power of Motion library, I created custom transitions between pages that aim to feel natural and engaging, helping to keep the user immersed in the website. Combining dynamic fade effects with content animation there’s a sense of movement and liveliness that complements this otherwise minimalist design.

The code related to these main page crossfades is located in a shared Layout component wrapping every page. It renders Motion’s AnimatePresence which dictates how the rendered pages will enter and exit — mount and unmount. It also provides handy callbacks that assure subsequent triggers and in this case state changes that orchestrate the main scroll controller. 

<AnimatePresence mode="wait">
  <motion.main
  key={asPath}
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.5, ease: easeQuadInOut }}
  onAnimationStart={onLayoutAnimationStart}
  onAnimationComplete={onLayoutAnimationEnd}
>
{children}
  </motion.main>
</AnimatePresence>

Custom navigation

The homepage acts as a welcome screen and is navigated away on scroll putting the projects on the forefront of the site. Similarly, the projects page represents an overview that allows an immediate glimpse into each portfolio piece but also allows for a quick way back to the splash screen by scrolling upwards. 

The challenge here was to combine the two scroll interactions – the WebGL scroll and the navigation scroll. For this purpose I relied on Lethargy scroll to detect proper scroll events and paired it with a custom scroll controller based on Virtual scroll that is used throughout the site as the main scrolling engine.

Video rendering and shader effects

With the increasing demand for high-quality media experiences, I ensured the support for high-definition video content running at 60fps and hosted it on R2 to guarantee quick load times. Videos are implemented with WebGL as textures. In order to allow for a higher video compression rate without noticeable artifacts, the fragment shader has an added LED overlay alongside the noise grain on top of each video. This also has the visual effect of presenting all the projects through the same lens, creating a consistent thread that ties the individual portfolio pieces together.

The displacement happening when loading and switching projects manipulates the vertices along the x axis using a noise-based pattern. At the same time, it creates a time buffer that allows the new videos to start loading while the transition is in progress. Additionally, an added sense of depth is created by applying a curved bend to the modelPosition in the vertex shader.

The projects media is rendered in a resourceful way by sharing plane meshes. If a project has fewer videos than another, the extra meshes are skipped and not rendered. This way the WebGL overhead is optimized as the number of meshes is always kept at the minimum.

To add to that, all the meshes share a single plane geometry between them.

const geometry = useMemo(
  () => new THREE.PlaneGeometry(planeSize.width, planeSize.height, 1, 128),
  [planeSize.width, planeSize.height]
)

Another visually interesting shader effect is a pseudo-noise-like motion of the segmented pieces in the background — essentially a time-based visual pattern that adds movement. Here it is emphasized.

Infinite smooth scrolling

The about page is an editorial bit and it features an infinite scroll/loop — an interaction I really like to see used in the right context. Here, it’s based on the shared scroll engine retaining the same fluid feel between the WebGL grid on the projects page and DOM movement on the about page.

Creating a smooth scrolling effect can be done in different ways today, some of them more accessible than others and each having their own use. For the purpose of this project, and again exploring and having fun, I wanted to take on making a reusable infinite scrolling concept that works on touch devices as well. 

The gist of it is — A completely arbitrary page content is passed in as the component input and gets segmented into sections. Based on the rendered dimensions of each section the data is stored — height and top offset of each section. As we now gather the total size of the entire page, the scroll logic keeps track of this setup and translates only the currently visible section related to the progress, the rest are idle. This allows for optimal DOM manipulation as transforms are applied in the frame loop only when needed. This scrolling technique has touch and resize support and allows for content of any shape and size.

Mobile version

The mobile version retains the same UI interactions and motion principles with the exception of the WebGL layer. This assures a more lightweight site leveraging native HTML5 video elements and browser-optimized content loading. To be more in line with the desktop version, other visual elements are added such as videos grid on the projects page.

Wrap up

That’s it! I hope it was a fun read.

You can check out the site here. If you have any questions or want to say hi or build something together, feel free to reach out to me through my email. And one for the road—use .gif as your open-graph images. It’ll animate where supported.

Read Codrops!

Check out our other content

Check out other tags:

Most Popular Articles