The Creative Pass Watch all courses for just $12/month

Mar 11, 2026

Bruno’s Portfolio Case Study

Bruno’s Portfolio Case Study

In 2019, I launched my first portfolio. It was a simple world built with Three.js, where visitors could drive around in a red car and crash into things. Nothing groundbreaking. Yet it went viral and jump-started my career.

Still, I knew I could push it further. So five years later, I began working on a new version.

Rebuilding my portfolio for the WebGPU era

People mostly know me for that portfolio, so I decided to keep the same core concept, but make it ten times better.

Recently, Three.js has undergone major updates following the adoption of WebGPU as an alternative to WebGL. One of those updates is TSL, a way to write shaders directly in JavaScript. This portfolio felt like the perfect opportunity to level up my TSL skills.

The challenge with building a portfolio is that there’s no client. In fact, you could argue we’re our own worst client. That often leads to endless projects because it’s never quite good enough, and there’s no real deadline. To avoid that trap, I set a release goal for the end of 2025.

To raise the stakes even further, I decided to document the entire journey through devlogs. It’s a great way for me to improve my video editing skills, and you can follow the series on my YouTube channel.

And finally, I wanted to introduce multiplayer features.

A world made in Blender

I’ve been using Blender for years now, and it’s proven to be the right tool for building 3D websites and games. It’s flexible, stable, scriptable (if you know Python), and backed by such a massive community that virtually every question already has an answer.

So I built the entire world in Blender.

Preview of the portfolio world in Blender
Preview of the portfolio world in Blender

I relied heavily on naming conventions to control the behavior of the world’s components. For example, an object named “refLandingPhysicalFixed” is automatically converted into a fixed physics body and exposed in JavaScript as “landing”.

I also used Empties extensively for collision shapes, respawn points, area boundaries, and more.

On top of that, I went a bit further to make sure the preview in Blender closely matches the final output. I added grass, tiles, dirt, and water so I can easily tweak the environment and paint the terrain with confidence.

Designing stylized nature

I’ve always been fascinated by stylized 3D nature and the many techniques you can use to achieve that look.

I started with the grass. Each blade is made of a single triangle, and there are around 78,400 blades in total, so roughly 78,400 triangles. That’s light enough to fit into a single geometry without putting unnecessary pressure on GPU memory.

Covering the entire island with grass is actually a trick. I only used a limited number of blades and loop them seamlessly toward the sides, so the ground always appears fully covered from the camera’s perspective.

Water areas are painted directly in Blender, and I reused that texture to generate shoreline ripples.

For the trees, I created three variations: birch, oak, and cherry. The trunks are standard 3D geometries, but the foliage consists of camera-facing planes. The leaves are rendered using an SDF texture, which gives me precise control over their size. That made it possible to dynamically shrink the leaves in front of the car, so the player can clearly see where they’re going.

And finally, everything reacts to the wind.

Simulating Time and Seasons in Real Time

Speaking of wind, there’s a full weather system, complete with day–night cycles and changing seasons. And everyone experiences the same conditions at the same time.

The wind sweeps across the island, with lines moving through the air, grass blades swaying, and tree foliage shaking.

Rain falls and splashes against the water’s surface.

When temperatures drop, rain turns into snowflakes. Snow begins to pile up, and the water freezes.

In the fall, leaves drift down from the trees.

And when the air gets heavy and overcast, you might find yourself caught in a lightning storm.

It’s not a portfolio if there is no project

I had two types of projects I wanted to showcase: client work and experiments (which I call the Lab). To preserve the immersive feel of the portfolio, I wanted to avoid traditional HTML UI as much as possible.

That meant building actual 3D models for the interface and recreating standard interactions entirely in 3D, including clicks, scroll, keyboard input, and touch gestures. You can even navigate it with a gamepad.

Multiplayer, but with limitations

As much as I would have loved to put every player and their car into the same shared world, that wasn’t realistic. First, I expected hundreds of visitors at launch. Second, since it’s a physics-based game, allowing physics-driven players to interact with one another would require significant server-side computation. Third, malicious users would inevitably find ways to disrupt the experience.

So instead, I opted for more subtle multiplayer features.

The first one is Whispers. These small flames can be placed by any user and display a short message alongside the user’s country flag. There can only be 30 whispers active at a time. They’re moderated using OpenAI’s free moderation model, and they can’t be placed in spots that would interfere with other players.

Next are the cookies. As a joke, I added a cookie stand where players can “accept cookies.” The number displayed above the cookie oven is actually a global counter shared by everyone.

Then there’s the altar. Falling into the abyss increments a counter as well, but this one has a special twist that I won’t spoil.

Finally, there’s the circuit leaderboard. Since I wanted the portfolio to feel more like a real game, it needed concrete goals. A racetrack felt like the perfect fit for the car.

To raise the stakes, lap times are saved, and you can see the top 10 displayed near the starting line.

Leaderboard

The leaderboard resets every day.

When performance defines possibilities

Most of the time, performance is the real constraint on creativity. Adding hundreds of cool features isn’t the hard part, but if the experience runs at 10 FPS, no one will stick around. It’s even more challenging on the web than in native games, because users expect instant access to information, and it has to run smoothly on their device no matter how powerful it is. That includes mobile devices.

So I used every optimization technique I could think of.

I relied heavily on instancing for trees, foliage, benches, lanterns, bowling pins, bricks, and more.

As mentioned earlier, the grass blades loop to always fill the camera’s view, but the same approach applies to the water surface, terrain, and even the rain.

Non-visible areas are frustum culled.

Models are manually optimized: hidden faces are removed, geometries are merged, and colors are preserved using a palette technique. The palette is a texture that contains all the scene’s colors, and each model retrieves its color through UV mapping. This allows color variations across merged geometries without relying on vertex colors.

Textures are kept as small as possible and compressed using GPU-friendly formats like ETC1S and UASTC.

Comparison of PNG against ETC1S and UASTC
Comparison of PNG against ETC1S and UASTC

Models are compressed with DRACO, primarily through quantization.

On mobile devices, the experience automatically switches to a lower quality preset: water blur and depth-of-field effects are disabled, and shadow map resolution is reduced.

Finally, thanks to TSL, the Three.js shading language, the experience automatically runs on WebGPU when available, delivering better performance without any additional work.

Sound design to convey emotion

Ever since I started working on the web, I’ve felt that we don’t pay enough attention to sound design. That’s a missed opportunity, because sound is one of the most powerful ways to convey emotion. Imagine movies without music, or games without audio. Beyond atmosphere, sound is also a powerful UX tool which it provides cues, feedback, and clarity.

I commissioned Kounine, a teacher and composer, to create three beautiful tracks. They’re peaceful, with a subtle sense of adventure, and they fit the experience perfectly. They’re released under a CC0 license, so you’re free to use them however you like.

https://github.com/brunosimon/folio-2025/tree/main/static/sounds/musics

From there, I layered in sound design across the entire world, starting with environmental audio like birds, crickets, waves, wind through the leaves, thunder.

Then come object-based sounds: the anvil, oven, campfire, bowling pins, crate explosions, collisions, circuit elements. All of them are spatialized, so if a sound emitter is positioned to your left, you’ll hear it through your left speaker.

The vehicle alone has many sounds like springs, hydraulics, engine noise, tire friction against the ground, boost, and even a horn.

For the UI, I kept it minimal and used a single click sound, simply adjusting the playback rate when closing elements to create subtle variation.

To create more complex sounds, like the intro, or when we navigate between the projects, I used Garage Band. It’s a powerful software, but easy enough for beginners like me.

Turning a Portfolio into a Game

I’ve seen people record themselves speedrunning my previous portfolio. Their goal was to clear the jump as fast as possible, and I found it hilarious. But I also wanted to give them a real challenge, so I introduced achievements.

It’s a collection of objectives: some easy, some difficult, and some so cryptic you might not even realize what you’re supposed to do.

Completing achievements unlocks vehicle skins as rewards and you can see how long it took you to complete them all.

More than a year, but done

It took me a little over a year to finish the project, but I’m genuinely proud of the result. I implemented every feature I had envisioned, it performs well on most devices, I learned an incredible amount along the way, and users seem to truly enjoy the experience.

The front-end code is available on GitHub under the MIT license. Feel free to use anything you find useful.

About Bruno

Bruno Simon sits at the intersection of, creative coding, technical art direction, front-end engineering, experiential web design.

He also teaches advanced Three.js and WebGL (soon WebGPU) concepts, helping developers move from static UI thinking into spatial, real-time rendering workflows.