bur1alrites
- Period
- 2026
- Contribution
- Design and development
- Status
- Live client portfolio
A video-led portfolio for Allie's live lighting work, combining an interactive gallery, a 3D projector room, and an automated playback pipeline.
A site that reacts
My friend Allie creates real-time, audio-reactive lighting for events. She wanted a site that responded like her shows.
I designed and developed the interface, scenes, and media pipeline. The background responds to video brightness and pointer input, with splashes on click. Allie chose the typeface and supplied visual references, giving us a starting point. Her lighting is audio-reactive; the site's visual response uses video and pointer input.


Moving through the work
The gallery introduces spatial depth before a visitor enters the projector room. Three.js, through React Three Fiber, places video previews in the scene with slight parallax. Inverted previews reveal their colour on hover; choosing a clip changes the scene and opens that video in the room.

The projector room
Opening a clip takes you into a room built around a projected image. Bloom lets the light spill beyond the screen.
The supplied footage was heavily compressed. I used animated grain to make the artifacts less conspicuous and give the projection its texture. Grain changes how the footage is presented; it does not restore lost image detail.

Balancing video and 3D rendering
Video decoding and the 3D scene compete for browser resources. An automated encoding worker prepares HLS playback renditions while preserving the masters. A viewport pool preloads visible previews at the lowest rendition, stops idle buffering, and lets only the active preview upload frames into the scene. Offscreen streams are released.
Rapid scrolling creates an ownership problem: a stream can finish attaching after its card has left and returned with a new player. The viewport video pool checks instance identity before accepting that completion and destroys stale controllers. Its lifecycle regression test recreates that sequence and asserts that the old controller is destroyed while the replacement remains alive. Another case protects rapid leave and re-entry while a play promise is pending.
Recovery is bounded: an active preview gets at most two delayed automatic retries, and leaving the viewport or hiding the page cancels recovery work. Autoplay denial does not start a retry loop; the poster remains until playback produces a frame. HTML/mobile previews and the WebKit-safe rendering path retain the same viewport loading policy. These are implementation provisions; the mocked player tests do not establish real-device codec support or frame rate.
A separate quality controller responds to sustained frame times. Lower presets reduce pixel ratio, reflections, geometry detail, and bloom work. Upgrades require a longer stable interval than downgrades, trading some visual detail for less frequent quality switching. Synthetic-frame tests check that policy; they are not measurements of GPU performance.
Maintaining the archive
Next.js keeps public pages and admin routes in one app. Allie can upload MOV or MP4 masters through multipart transfers to R2, then a separate worker creates playback versions. Failed parts and finalization can be retried while the page stays open; upload sessions are not restored after a reload.
The result is a maintainable clip archive with two connected viewing modes: a gallery for choosing work and a projector room for watching it. This was my first Three.js project, and coordinating browser video lifecycles with the spatial scene became as important as building the scene itself. The source examples here are pinned to the October implementation; browser and device measurements would be needed before making a broader performance claim.