How I built a portfolio you can ride through.
A live 3D motorcycle ride in the browser: procedural terrain, nine story stops, a free-roam mode and a synthesised soundtrack. Here’s how it came together, step by step, and how I planned it like a delivery.
- story stops on the road
- 9
- downloaded 3D models
- 0
- audio files
- 0
- to the facts, via Portfolio
- <20 s
Start with the story, not the stack.
Most portfolios are a list. I wanted mine to feel like the way I actually work: a long road with deliberate stops, each one a program I delivered.
The brief I wrote for myself had three rules. A recruiter must reach the facts in under 20 seconds. Anyone curious should find something worth exploring. And the whole thing has to run in a normal browser, on a normal phone, with no install.
- The road is the career. Scroll moves you forward in time and distance, from Bangalore to a campfire at the end.
- Each stop is a case study. A landmark in the world makes the result physical: lanterns for markets launched, a gate for a new country.
- A shortcut for busy people. The “Portfolio” button skips the ride entirely.
Pick a small, boring stack.
No game engine, no bundler, no build step. Plain ES modules loaded straight into the page.
Three.jsScene graph, cameras, materials and shadows on top of WebGL.
WebGL + GLSLCustom shaders for fog, the lake surface, wind and colour grading.
JavaScript modulesThe world, the audio and the UI are three separate files.
ReactThe interface layer: HUD, panels, watch, settings and the portfolio page.
Keeping the world, audio and UI in separate modules meant I could rebuild one without touching the others, which mattered every time I added a stop.
// index.html loads the world lazily, after first paintimport('/assets/js/world-v7.js').then(m => { this.world = m.createWorld(host, { mobile, notes, ... });});import('/assets/js/audio-v3.js').then(m => { this.score = new m.Score(new m.Radio());});Grow the world from maths.
Every hill, tree, rock and cloud is generated when the page loads. Nothing is a downloaded 3D model.
The road is two sine waves added together, one for gentle curves and one for tighter bends. The terrain is noise, pressed flat near the road so the bike never floats. Trees, grass and flowers are scattered with a seeded random generator, so the world looks the same for everyone.
// the whole road in two linesexport const roadX = z => 38 * Math.sin(z * 0.011) + 16 * Math.sin(z * 0.027 + 1.3);export const roadY = z => 5 * Math.sin(z * 0.005 + 0.5) + 2.5 * Math.sin(z * 0.013);- Low-poly on purpose. Flat shading hides the low triangle count and gives the world its paper-craft look.
- Instancing everywhere. Thousands of trees, grass blades and flowers are drawn in a handful of calls.
- Biomes by distance. Highway and canyon, then forest, lake and coast, so the scenery changes as the story does.
Turn every case study into a landmark.
A border gate lifting as you ride through sticks better than a stat on a slide.
Each stop has a landmark tied to its result, a floating headline in the world, and a panel with the facts. Landmarks react to your progress: they switch on as you arrive, not before.
// Liquid I.V.: each lantern lights once you pass its thresholdprogFx.push(tt => { const on = tt > th; lan.material = on ? warmBasic : darkM;});Make scrolling feel like riding.
Scroll position becomes a target. The bike eases toward it with a little momentum, so a flick feels like throttle and a stop feels like braking.
// UI: scroll → a target between 0 and 1const u = scrollY / (document.documentElement.scrollHeight - innerHeight);world.setTarget(u); // World: accelerate harder the further away the target isconst gap = (target - t) * L, vmax = 34 + Math.max(0, Math.abs(gap) - 60) * 0.6;vel += (clamp(gap * 1.4, -vmax, vmax) - vel) * (1 - Math.exp(-dt * 2.2));t = clamp(t + vel * dt / L, 0, 1);- The camera slows down at stops and swings to the side so the landmark and the panel share the frame.
- On phones, scrolling snaps to the nearest stop, and big previous and next buttons replace the dots.
- The watch counts down the kilometres to the next stop and buzzes when you arrive.
Let people leave the road, and give them reasons to.
Explore mode turns the story into an open map with jungle, savanna, blossom valley, farmland, a snow peak with an ice cave, a lake, ruins and a coast. You can throttle, steer, jump and boost anywhere, and the map gives you things to find.
On desktop it uses the keyboard; on phones, a thumb joystick and a jump button. After a few seconds (straight away on phones) the HUD folds into a slim bar with your speed, level, today’s challenge, a hot-or-cold treasure readout and one-tap Journal, Photo, Call, Bell and Exit, so the view stays clear. The map lives inside the watch.
- Orb trails. Gold orbs line the side roads and run out to landmarks, so there is always somewhere to head. Five in a row triggers a short speed boost.
- A treasure hunt. Six chests are buried around the map, with one riddle at a time and a hot-or-cold meter. Each chest holds a line about a real project.
- Postcards. Ten scenic views glow pink. A photo taken inside one goes into the journal as a postcard.
- Progress that shows. 16 lanterns, 5 secrets, 12 stamps, 30 animals to spot, 13 challenges and a daily challenge with a streak. Everything earns XP toward seven levels, each with a level-up screen, and the journal can export a shareable card.
MujaSauros, the small dinosaur riding pillion, got a brain to match the bigger map. He remembers returning visitors and how often he has been petted, comments on each new region, points you toward places you have not seen yet, and smells buried treasure from about 100 m away. When you slow down near a chest, he jumps off and digs it up. He also sneezes in the blossom valley, yawns at night, tilts his head when you stop and shakes off after water.
Design the interface like a dashboard.
The overlay has one job: tell you where you are and what matters here, without covering the ride.
- One surface style for every panel: solid dark, a thin border, a soft shadow and the same button heights everywhere.
- The watch is navigation, a music player and an easter egg. Tap its face five times to crack it open.
- Messages, not speech bubbles. MujaSauros talks through notifications that slide in above the watch and buzz it, so his lines stay readable at any camera angle. Unlocks pop up with the XP they earned and a short synthesised chime.
- Settings hold the adaptive score; toggles for nature ambience, campfire crackle, braking sound and reward chimes; Auto or Low graphics; Full or Reduced motion; world controls for the day cycle, season and weather; and one button that silences everything.
Compose the soundtrack in code.
There are no audio files. The score, birds, wind and campfire crackle are all synthesised with the Web Audio API.
The music follows the ride: it thins out on the highway, blooms in the forest and settles into a warm drone at the campfire. Arriving at a stop plays a short chime, and braking hard gives a tyre scrub. Every layer can be switched off in settings.
Budget performance like a project.
A beautiful scene that stutters on a mid-range phone is a failed launch. The ride watches its own frame time and adjusts.
// quality tiers, measured every 60 framesif (ms > PERF_DOWN && tier > 0) setTier(tier - 1); // drop resolution, shadows, draw distanceelse if (ms < PERF_UP) setTier(tier + 1); // recover when there is headroom- Deferred building. The road and landmarks appear first; grass, flowers and critters stream in over the next few frames.
- Low-power detection. Devices with 4 cores or 4 GB of memory start on a lighter tier. Budget GPUs are recognised by name, start at 75% resolution and are capped at 30 fps to save heat and battery.
- Resolution as a last resort. On the lowest tier, if frames are still slow, render resolution steps down to as low as half, then climbs back when there is headroom.
- Only move what moves. Matrix updates skip static and hidden objects, instanced sets are split into spatial chunks for culling, and terrain streams in tiles as you ride.
- Survives a lost GPU. If the phone drops the WebGL context under memory pressure, the page lets the browser restore it instead of going blank.
- Crisp UI on every screen. Text and panels render at full device resolution even when the 3D scene steps down.
Always give people the short version.
The ride is optional. One button opens a plain, fast, indexable page with everything a hiring manager needs.
The portfolio page carries the case studies, client sites, career, toolkit and two clear routes to get in touch: full-time or freelance. It is also what search engines read, so the ride never hides the substance.
15 free tools, on the same site.
The same static site carries a set of free browser tools for everyday work. No sign-up, and nothing to install.
54 ATS-friendly templates
Build a resume in the browser and download it as PDF or Word.
Open the Resume Maker116 SOP templates
Pick a ready-made procedure, edit it, and export it to Word or PDF.
Open the SOP MakerInvoices, QR codes, passwords and more
An invoice generator, a project estimate calculator, an image resizer, a time zone meeting planner and nine more.
See all 15 toolsI plan, build and ship things like this for clients.
Program management for multi-market launches, or a hands-on build for your next site or experiment.