Logbook

Deploys you can read on a receipt.

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

Field notes

A changelog page where every release prints out of a receipt printer. The paper curls over the top in WebGL, ALL / NEW / FIX reprint it, and a flick or the tear key rips it off to flutter away. Real text sits under the paper for selection and screen readers.

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

Logbook is the changelog of Ferrule, a fictional deploy CLI. A receipt printer sits at the bottom of the screen and prints the release notes: version, date, a title, and every item tagged NEW, FIX, CHG, PERF or DEP. The paper rises out of the slot, rolls back over the top and hangs behind, and older releases print in lighter ink. The wheel, the arrow and page keys or a finger feed it; clicking a version on the left feeds straight to it. ALL, NEW and FIX on the printer tear the receipt off and print a new one under that filter, a fast downward flick (or ✂ Tear) rips it off to flutter away, and each receipt holds four releases before "OLDER ▸ TEAR" prints the next. The email form on the right prints a SUBSCRIBED ticket; onSubscribe in config.ts is where you wire your provider.

Until someone moves the mouse, touches the page or presses a key, it plays its own 10-second tour: a cursor presses NEW, the receipt tears and flies, the NEW receipt prints and rolls over, then ALL tears that one too and prints back to where it started. The product, releases, receipts-per-page and form copy live in one file. On phones the versions become a row of chips and the form sits above the printer; reduced motion gets fades and paper that is already printed; without WebGL the receipt is plain HTML and every key still works.

How it's built

  • One monospace grid lays out every receipt (JetBrains Mono is 0.6 em wide, so wrapping is arithmetic), and the same lines feed both the canvas texture and a transparent DOM copy over the flat part of the paper, which is what text selection and screen readers use. The curl itself is not selectable.
  • Each sheet is one plane bent in a vertex shader: rows past the curl line wrap onto a 58 px cylinder to 180° and then hang straight down behind, rows still inside the printer are discarded, and the torn edges are 3 px teeth cut in the fragment shader.
  • While the paper feeds, lines leaving the slot come out light and darken within 40 px, and each older release is painted 9% lighter, down to 55%.
  • A torn sheet is one rigid sheet: tossed up and aside with a seeded spin, stepped at 120 Hz from its age every frame, so the recorder can jump to any time. The whole tour is a pure function of time and the ripple turns a whole number of times per loop, so frame 0 matches frame 10 pixel for pixel.
  • The texture scale is capped by the GPU's texture limit, and four releases of typical length per receipt keep it at 2x on phones. A fast flick tears with mouse, pen or finger, a slow mouse drag is left to selecting text, and quick tears keep up to three sheets in the air at once.

Inside the prompt

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

logbook-changelog/
├─ .gitignore
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
   ├─ App.tsx
   ├─ config.ts
   ├─ index.css
   ├─ main.tsx
   ├─ vite-env.d.ts
   ├─ components/
   │  ├─ Ghost.tsx
   │  ├─ Printer.tsx
   │  ├─ Receipt.tsx
   │  ├─ Subscribe.tsx
   │  └─ Versions.tsx
   ├─ gl/
   │  └─ paper.ts
   └─ lib/
      ├─ engine.ts
      ├─ layout.ts
      ├─ loop.ts
      ├─ paint.ts
      ├─ sim.ts
      ├─ time.ts
      └─ timeline.ts

Recut it

  • Rename the product and rewrite the releasessrc/config.ts → PRODUCT / SITE / TAGLINE / RELEASES
  • Wire the email form to your providersrc/config.ts → onSubscribe / SUBSCRIBE
  • Releases per receiptsrc/config.ts → PER_RECEIPT
  • Change the colourssrc/index.css + src/lib/paint.ts + src/gl/paper.ts → @theme / PAPER, INK / paper-back colour in FRAG
  • A tighter or looser curlsrc/gl/paper.ts → CURL_R
  • Make the torn sheet fly furthersrc/lib/sim.ts → GRAVITY / DRAG / fallAt()
  • Retime the demo toursrc/lib/timeline.ts → PRESS_NEW / PRESS_ALL / REST_FEED

Also in the vault