Sift

Pour in an hour.

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

Field notes

A hero that is the inside of an hourglass. A brass spout sweeps the top edge and the headline piles up in sand, column by column; turn the glass and it falls to the floor and runs out through a slit. Brush it, tilt it, shake it, or start a real focus session and watch the words drain.

  • Particles
  • WebGL shader
  • Pointer interaction
  • Dark theme

Sift is the hero for Stillsand, a fictional focus timer, and the whole screen is the lower bulb of an hourglass. A brass spout walks along the top edge and pours the headline in sand: each grain falls, lands and settles, so the letters pile up from the bottom of each column. Click, scroll or press the down arrow and the glass turns: the words collapse to the floor and run out through a slit, and the next line pours. Drag through the sand and it parts around your pointer, then slumps back. Move the pointer sideways and the glass tilts and the stream leans; shake the pointer, or the phone itself, and every grain jumps and lands where it was.

The button is not a prop. Pick 25 or 50 minutes, or a one-minute demo, and the headline drains one grain at a time for the whole session, top surface first, while the clock and the tab title count down; End early hurries the rest out. Two empty functions in session.ts are where a real product connects its backend. Reduced motion shows each line already settled and cuts between lines, and in a session fades the grains where they lie instead of dropping them. Without WebGL the headline is plain text that a session clips from the top.

How it's built

  • Every grain is a pure function of time. Each one carries its target, the moment the spout releases it, when it drains and its turn in a session, and one vertex shader works out where it is: falling under gravity, a small bounce on landing, dropping to the floor, running along it to the slit. There is no simulation, so any frame can be drawn from the clock alone and the 10-second preview loops exactly.
  • The headline is drawn once into an offscreen canvas at the page size and sampled on a jittered grid; every opaque sample becomes a grain. Sorting the grains by 9-pixel column and then bottom up is what makes the spout sweep left to right and each letter pile up from its base.
  • A focus session drains the grains in order of height, top first, at an even rate over the chosen minutes. End early splits that order at the fraction already gone and squeezes the rest into 1.4 seconds; the two pieces meet at the split, so nothing jumps.
  • Tilt and shake never move a target. The tilt's lean is shaped to be zero where a grain lands (settled grains only shift a few pixels while the glass leans), and a shake throws each settled grain up on a parabola that ends where it started, so neither needs any code to put things back.
  • The state keeps when things were asked for, never how far along they are, so frame(state, t) is pure and a Node check asserts that second 10 equals second 0, that every grain lands before the subline appears and that End early is continuous.

Inside the prompt

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

sift-hero/
├─ .gitignore
├─ check.ts
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
   ├─ App.tsx
   ├─ index.css
   ├─ main.tsx
   ├─ vite-env.d.ts
   ├─ components/
   │  ├─ Page.tsx
   │  └─ Scene.tsx
   ├─ lib/
   │  ├─ copy.ts
   │  ├─ grains.ts
   │  ├─ live.ts
   │  ├─ look.ts
   │  ├─ loop.ts
   │  ├─ session.ts
   │  ├─ time.ts
   │  └─ timeline.ts
   └─ scene/
      └─ sand.ts

Recut it

  • Rename the product and rewrite every stringsrc/lib/copy.ts + index.html → COPY / <title>
  • Write your own three headlinessrc/lib/copy.ts → SLIDES (two short lines and one sentence each)
  • Change the session lengths or the demosrc/lib/copy.ts → CHOICES
  • Connect sessions to your backend or analyticssrc/lib/session.ts → onSessionStart / onSessionEnd
  • Re-colour the sand, the dusk and the brasssrc/lib/look.ts + src/index.css → SAND / @theme
  • Make the pour heavier, the drain faster or the grains finersrc/lib/look.ts → PHYS / sampleStep / grainSize
  • Retime the pour, the hold and the drainsrc/lib/timeline.ts + src/lib/grains.ts → SETTLE / HOLD / DRAIN / BUILD
  • Tune the brush, the tilt and the shakesrc/lib/look.ts → BRUSH / TILT / SHAKE
  • Check the loop still closes and the copy still fitscheck.ts → pnpm check

More in Hero Section

All hero section templates →