The idea was born from a single question: what if a website had no boundaries?
Most 3D web experiences anchor the user to a fixed perspective — a hero section, a scroll journey, a product viewer. I wanted to build something that felt genuinely infinite and alive — where the environment reacts to you, where geometry defies physics, and where the boundary between UI and universe dissolves.
Two creative obsessions collided here:
M.C. Escher's impossible architecture — staircases that loop forever, gravity that pulls in six directions simultaneously, rooms with no correct "up."
Synesthetic sound-visuals — the idea that moving your mouse should feel like stirring liquid light, that a gravity flip should sound like the universe groaning under stress.
The web browser felt like the perfect canvas. Zero installation. Universal access. And with WebGL, WebAudio, and WebAssembly all maturing simultaneously — finally powerful enough to realize this vision.
🛠 How It Was Built The stack was chosen to maximize visual fidelity while keeping the entire experience 100% in-browser with zero assets — no textures, no audio files, no 3D models downloaded.
The Fluid Orb The centrepiece is a procedural liquid orb built entirely in GLSL shaders. The vertex shader runs 5-octave Fractional Brownian Motion using 3D simplex noise to physically displace every vertex on the sphere, every frame. The displacement scale reacts live to your mouse velocity — move fast and the orb erupts into turbulence.
The fragment shader implements physical thin-film interference — the same optical phenomenon that makes soap bubbles iridescent — computing RGB colour based on the viewing angle and an animated thickness value. Triple specular highlights, animated surface vein lines, and a subsurface rim glow layer on top to give the orb its molten plasma feel.
The Escher Labyrinth The labyrinth uses the Rapier physics engine (compiled to WebAssembly) to simulate real rigid-body dynamics inside impossible geometry. The key insight: gravity is just a vector. By exposing all six axis directions as interactive controls, clicking any wall instantly re-orients both the physics world and the camera — creating the Escher illusion that you fell sideways, not the room. GSAP animates the camera quaternion smoothly during the 1.4-second transition so the flip feels cinematic rather than jarring.
The Procedural Atmosphere The skybox is a full-screen sphere with a custom fragment shader interpolating between three cinematic palettes:
Dawn — deep indigo zenith, burnt orange horizon blush Golden Hour — navy sky, rich amber horizon Neon Night — near-black void, violet-cyan nebula clouds, layered star field, Milky Way band The time value is driven by the real-world clock, mouse scroll, or the HUD scrubber — all feeding through a smooth lerp to avoid jarring jumps. A volumetric fog controller and an orbiting sun/moon celestial body complete the atmosphere.
The Audio Engine The ambient drone is three detuned oscillators (55 Hz, 82.4 Hz, 110 Hz) routed through an algorithmic convolution reverb with a synthetically generated impulse response — all via Web Audio API, zero audio files. Mouse speed modulates a low-pass filter cutoff in real time, creating synesthetic feedback where fast movement brightens the timbre. Every interaction triggers a unique procedural SFX: a deep sub-bass plunge on gravity flips, a crystalline shimmer chord on scene transitions, a tactile ping on physics spawns.
🧗 Challenges Faced Correct normals on a displaced mesh
The biggest shader challenge: a displaced vertex has a different normal than the original sphere. Using the raw sphere normal gives completely wrong specular highlights. The solution was computing two finite-difference neighbours along the tangent and bitangent on the GPU, then taking their cross product — recalculating analytically correct normals every frame in the vertex shader.
Rapier WASM bundle size
The physics engine compiles to ~3 MB of WebAssembly. Shipping this on first load would make the experience feel broken. The fix was lazy-loading the entire EscherLabyrinth component with React's lazy() + Suspense, combined with a silent 1.5-second prefetch that begins while the user is still exploring the macro void — so by the time they click the orb, the chunk is already cached.
Audio Context autoplay policy
Modern browsers block AudioContext creation until a user gesture. The audio engine is wired to a toggle button that acts as both the gesture trigger and the state setter — ensuring the context is always created inside a trusted event handler, never speculatively.
Post-processing with transparent geometry
The bloom pass amplifies all emissive fragments, but transparent objects with disabled depth writing can create sorting artifacts with the depth-of-field pass. Each transparent mesh required careful render ordering to avoid ghosting halos around the orb and rings.
📚 What I Learned GLSL is a full creative medium. Writing a physical thin-film interference model from first principles and watching iridescent soap-bubble colours emerge in real time on a sphere was genuinely surprising. Physics as art direction. Rapier's gravity vector is technically just a vec3 — but framing it as a 6-axis creative control transforms it into an interaction design primitive. Web Audio scheduling is musical. The API's sample-accurate event scheduling enables genuinely expressive procedural SFX without any audio files — something I deeply underestimated before building this. Bundle splitting is UX design. Deciding which code loads when is as much a user experience decision as any visual choice.
Log in or sign up for Devpost to join the conversation.