Case Study, Interactive 3D
305 Studios
My studio portfolio built as a single 3D scene you move through by scrolling: a temple garden at night, rendered live in the browser with Three.js, with the work and experience set into the scene itself.
Three.js WebGL GLSL JavaScript Design & Build
Project Overview
305 Studios brings my work, services and experience into a continuous interactive portfolio. Visitors can choose Temple, Redwoods or Forest, explore the projects through detail panels, and switch to View Scene to enjoy the environment on its own.
I direct the visual experience and interface, collaborating with AI on modeling, development and iteration. The current scenes combine a detailed Blender temple model and baked lighting with browser-rendered landscapes, water, foliage and atmospheric effects. I review the result for responsive behavior, clear navigation and usability.
The application shell, camera rig and post-processing build on Kage from Meng To’s MIT-licensed ThreeUI Community. The project-card fabric is adapted from Canvas UI’s Cloth.
with AI assistance
Implementation
A Scene You Scroll Through
The camera travels a spline through the scene, and the scroll bar is the only thing that moves it. Each chapter of the site is pinned to a point along that path, so the story is climbed rather than read: the roles in my experience stand on the stair itself, oldest at the foot and most recent near the top.
Night scenes live or die on wet stone, so the surfaces are built for it. Planar mirrors render the world a second time from a reflected camera for the paving and the water, and the specular glints are written into the roughness map rather than the colour, so they only sparkle where a light actually catches them. The mirror camera is aimed with lookAt instead of a flipped matrix, so its triangles keep their winding:
Development note: planar reflections
/* Mirror the rig through y = M.planeY and render the world into M's buffer.
Reflecting a point about that plane keeps x and z and sends y to 2h − y */
function renderMirrorPlane(M, hide) {
const c = M.cam, h2 = M.planeY * 2;
_mPos.set(camera.position.x, h2 - camera.position.y, camera.position.z);
camera.getWorldDirection(_mTgt);
_mTgt.add(camera.position); _mTgt.y = h2 - _mTgt.y;
_mRot.extractRotation(camera.matrixWorld);
_mUp.set(0, 1, 0).applyMatrix4(_mRot); _mUp.y = -_mUp.y;
c.position.copy(_mPos); c.up.copy(_mUp); c.lookAt(_mTgt);
// …
}
The Work, In the Scene
Cards That Move Like Cloth
The three featured projects hang in the scene as cloth. Each card is its own cloth simulation carrying a real capture of the site it describes, so it drapes, billows and shades as the pointer brushes past, and its outline is drawn in the cloth shader so it follows every fold.
They stay honest links. A plain click opens a panel that describes the project before sending anyone away, while new-tab and modified clicks fall straight through to the browser, so the status bar still shows the destination and a reader without JavaScript still gets a working link. Games and design work sit in an archive beneath them.
Development note: project links and detail panels
$$('.card[data-work]').forEach(card => {
card.addEventListener('click', e => {
/* anything the browser has its own meaning for is left alone */
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
e.preventDefault();
openFeatured(card.dataset.work, card.getAttribute('href'));
});
});Results
The result is a portfolio that doubles as its own case study, live at threeohfivestudios.com/portfolio. It is a single HTML page with no build step, deploys through GitHub Actions only after its test suite passes, and degrades gracefully: a reduced-quality mode for lighter devices, reduced-motion support, and work cards that remain ordinary links.
See It Live
Experience the Full Site
Climb the stair from the gate to the contact, and switch between Temple, Redwoods and Forest along the way.