Creative development · UI/UX case study

Making a background feel alive.

I turned a mathematical interference-wave experiment into a responsive, scroll-directed 3D environment for my portfolio, then built the tools needed to tune it without losing the feeling that made it interesting.

RoleDesigner + developer
Built withThree.js, WebGL, GLSL
CollaborationIterated with Codex
OutputAdaptive live background
Read the case study

The brief

A visual system, not a decorative loop.

The first idea was simple: use a looping video as a lightweight website background. The project became more ambitious when I realized the camera itself could respond to the page.

The final experience keeps the waveform moving continuously while scroll advances a camera through a spiral path in 3D space. A separate tuner exposes the wave, camera, palette, path, and post-processing parameters. The live page watches its own performance and adjusts detail when needed.

400
Starting mesh segments
30 fps
Adaptive performance target
1 source
Shared configuration of truth
0 video
Fallback files in the final concept

The artifact is live

Scroll is moving the camera through the work as you read about it.

The fixed look-at point keeps the composition coherent while the camera path reveals new parts of the surface.

The evolution

The useful version appeared through experiments, not a straight line.

These frames reconstruct the major visual stages from the project’s settings and development history. They are labeled as recreations because the early prototypes were not originally archived as images.

01

Starting point

Two mathematical sources

A Wolfram Demonstration supplied the core equation: two cosine waves, each radiating from a point, summed into an interference surface. I translated that equation into a Three.js plane and removed random noise so the structure of the wave stayed legible.

  • Radial interference
  • Two sources
  • Vertex displacement
Recreated frame Early radial interference
02

Finding a voice

From symmetry to a folded landscape

Perfect rings felt like a simulation. I wanted something with more compositional tension, so the coordinates were folded, sheared, and blended with a directional wave. The resulting asymmetric surface felt less scientific and more like an identity system.

  • Asymmetric fold
  • Camera framing
  • Height-mapped color
Recreated frame Asymmetric fold and tuned palette
03

Texture studies

Grain exposed a color problem

Several grain systems were tested: static screen noise, multiplied luminance, surface-mapped textures, and palette-aware color grain. Black multiplication made the scene muddy; oversized mapped speckles made it look tiled. The strongest experiment moved each fragment a small distance along the existing color ramp instead of darkening it.

  • Surface mapping
  • Palette perturbation
  • Luminance preservation
Recreated frame Palette-aware surface texture
04

Final direction

The camera became the interaction

The project stopped being a background export and became a live environment. Scroll maps to a spiral camera function while the look-at point remains fixed. Raw scroll input is eased before it reaches the camera, preserving responsiveness without inheriting every abrupt trackpad movement.

  • Scroll smoothing
  • Spiral camera path
  • Adaptive quality
Current build Scroll-directed live WebGL scene

The tuning interface

I needed a visual instrument, not a file full of mystery numbers.

The tuner exposes the camera, scroll path, mesh, wave, palette, grain, and post-processing settings. Its guide frames reveal how the same composition crops across desktop, tablet, and mobile before a value is committed to the shared configuration.

Open the live tuner

What did not ship

Dead ends made the final product smaller and clearer.

A

A 90-second video fallback

I built frame-stepped capture tools, ffmpeg streaming, WebCodecs experiments, logging, headless Chromium rendering, and completion states. At the desired resolution, the file remained too large and removed the most interesting interaction: moving through the scene.

Decision: remove the fallback entirely.
B

Device capability before interaction

An earlier plan locked scrolling while a hidden renderer benchmarked the device, then swapped between video and WebGL. It solved camera snapping but introduced loading complexity and an awkward first-use state.

Decision: start live and adapt continuously.
C

Grain as the defining visual effect

Grain looked compelling in references, but repeated attempts revealed aliasing, banding, muddy luminance, and scale problems on a moving 3D surface. It remains available in the tuner, but the design no longer depends on it.

Decision: treat texture as optional, not structural.

Working with Codex

I used AI as an implementation partner, not an idea generator.

1Observe

I described what felt wrong in visual language: muddy, too fast, snapping, tiled.

2Instrument

Codex exposed controls, logs, performance counters, and render diagnostics.

3Compare

I tested changes live and brought back specific visual feedback and references.

4Converge

We promoted useful experiments into shared settings and removed brittle branches.

“The most productive prompts were not requests for polish. They were precise descriptions of perception: the camera feels too high, the grain is mapped too large, the frame darkens when the effect turns on.”

Technical evolution

A small rendering system grew around one equation.

01Shared configuration

Wave, camera, path, palette, quality limits, and effects.

02Wave renderer

CPU vertex displacement with a GLSL color and effects pipeline.

03Experience layer

Tuner previews the path; portfolio scroll drives the same function.

Wave model

Displacement before decoration

Each mesh vertex samples the selected wave function. Heights are normalized every frame, allowing the complete palette to remain visible even when amplitude or damping changes.

Camera model

One path, two interfaces

The tuner’s path scrubber and the live page call the same spiral-camera function. That removed the framing mismatch that appeared when each page implemented camera movement independently.

Performance model

Quality can move both directions

The renderer targets 30 fps. It reduces mesh density before device pixel ratio, never drops below 300 segments, and raises quality again when the device demonstrates sustained headroom.

Post-processing

Effects without a darker frame

Chromatic aberration, diffusion, and halation use a multisampled full-resolution render target. Matching Three.js color-space conversion prevented the post-process pass from dimming the entire image.

Outcome

The final background is also the documentation.

As you scroll this page, the live camera follows the same path described above. The performance readout in the corner is intentionally visible in this prototype so every design claim remains inspectable.

Explore the tuner