Halftone

Every diff, explained.

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

Field notes

A dark devtool hero whose artwork is printed live by a shader: a 3D object rendered as 1-bit dither, halftone dots or ASCII, with a wipe between modes and light that follows the pointer. Two tones and one accent, no purple. No images, no video.

  • WebGL shader
  • Pointer interaction
  • Developer tool
  • Dark theme

Halftone is a hero for a fictional AI code-review tool, built to look nothing like the purple-gradient AI default. A lumpy sphere with a thin ring and an orange moon turns beside the headline, and it is never drawn smoothly: a second shader pass prints it as 1-bit ordered dither, as round halftone dots on a 30° screen, or as ASCII characters, with a diagonal wipe and an orange line between modes.

Visitors switch modes from the Render control and steer the light with the pointer. Until they touch anything, the page runs a 12-second demo that shows all three. The object is raymarched in the shader and the characters come from a small glyph atlas drawn at load, so there are no images, models or video, and swapping the palette means changing three colours.

How it's built

  • Render low, print high: the object is raymarched into a target a third of the screen's size, then a second pass prints every screen pixel from it. Brightness rides in the red channel, and green flags the moon so it always prints in the accent.
  • Three print modes in one shader: Bayer 8×8 ordered dither, halftone dots on a rotated 30° grid sized by the square root of brightness, and ASCII from a ten-glyph atlas drawn after the mono font loads.
  • A wipe with a front: every pixel checks which side of a moving diagonal it's on and prints in the old or new mode, and a thin accent line marks the edge.
  • No speckle on black: the Bayer threshold is offset by half a step, since the matrix's zero cell would otherwise switch on at zero brightness and dot the whole page.

Inside the prompt

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

halftone-hero/
├─ .gitignore
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
   ├─ App.tsx
   ├─ index.css
   ├─ main.tsx
   ├─ vite-env.d.ts
   ├─ components/
   │  ├─ Halftone.tsx
   │  ├─ Hero.tsx
   │  └─ Scene.tsx
   └─ lib/
      ├─ glyphs.ts
      ├─ loop.ts
      └─ modes.ts

Recut it

  • Rename the product and rewrite the pitchsrc/components/Hero.tsx → Hero()
  • Swap the ink, paper and accent colourssrc/components/Halftone.tsx → uInk / uPaper / uAccent
  • Change the object: shape, ring, moonsrc/components/Halftone.tsx → sceneShader map()
  • Make the dots, dither cells or characters biggersrc/components/Halftone.tsx → printShader dither() / halftone() / ascii()
  • Use a different ASCII rampsrc/lib/glyphs.ts → RAMP
  • Retime the demo or the wipesrc/lib/modes.ts → scriptedWipe()

More in Hero Section

All hero section templates →