Darkroom

Salt & Sodium

Fig. 1 — Darkroom, live captureOpen the live demo ↗12 s loop · recorded frame by frame on GPU

Field notes

A dark photography portfolio where the photos sit on an endless WebGL film strip. Scroll or drag and the strip bends with the speed while the colour channels split; click a photo and it opens full-bleed with its title. Eight hosted photos included, swappable in one file.

  • WebGL shader
  • Scroll-driven
  • Pointer interaction
  • Portfolio
  • Dark theme

Darkroom is a portfolio for a fictional film photographer, built around one series: eight night photographs from the Atlantic coast. They sit on an endless horizontal strip of WebGL planes. Move it and it curves like film running through a gate, the red and blue channels pull apart with the speed, and every photo away from the centre drains to near-monochrome until it arrives. Click the centred one and it grows to fill the screen, with its title set large in Instrument Serif.

It works with a mouse wheel, a trackpad, a finger or the arrow keys, and snaps to the nearest photo. Until someone touches it, the page plays its own 12-second tour. The photos are hosted with CORS open so the prompt rebuilds anywhere, and replacing them with your own work is a list of URLs in one file. Anyone who prefers reduced motion gets the strip without the bend or the colour split.

How it's built

  • Bend by speed: the vertex shader pushes each vertex back by its world x squared times a uniform that grows with the strip's velocity, so a flick curves the whole strip like film in a gate and it flattens as it stops.
  • Colour split and fade in one pass: the fragment shader samples red and blue a little either side of green by the same velocity, then mixes towards grey the further a photo is from the centre.
  • An endless strip with no clones: each photo's distance from the centre is wrapped into -4 … 4, so eight planes loop forever and the demo's eight flicks land exactly where it started.
  • Photos any rebuild can load: they are hosted on basaltframe.com with Access-Control-Allow-Origin open and loaded with crossOrigin set; if one fails, a numbered canvas placeholder takes its place.

Inside the prompt

Every file below is written out in full, so your agent creates the project exactly — no guessing, no missing pieces.

darkroom-portfolio/
├─ .gitignore
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
   ├─ App.tsx
   ├─ index.css
   ├─ main.tsx
   ├─ vite-env.d.ts
   ├─ components/
   │  ├─ Gallery.tsx
   │  ├─ Overlay.tsx
   │  └─ Scene.tsx
   └─ lib/
      ├─ loop.ts
      ├─ photos.ts
      ├─ textures.ts
      ├─ timeline.ts
      └─ view.ts

Recut it

  • Put in your own photos, titles and captionssrc/lib/photos.ts → PHOTOS
  • Rename the photographer, the series and the linkssrc/components/Overlay.tsx → Overlay()
  • Tune the bend, colour split and fade to greysrc/components/Gallery.tsx → fragmentShader / useFrame
  • Resize the photos or change the gap between themsrc/components/Gallery.tsx → useLayout()
  • Change the colours and fontssrc/index.css → @theme
  • Retime the demo toursrc/lib/timeline.ts → offsetAt() / openAt()