Make a printed 5×5 in Chatbooks photo book from a project's collection (clips, albums, photos): catalogue with permanent IDs, lab and pick tools, spread-first layout, QR short links, 300 dpi export with bleed for Chatbooks' trim.
I'm a big fan of printed things: something you can hold, touch, flip through, stare at, and come back to, to reflect on or simply admire the work. These are personal projects, and I wanted them to have a physical form I can keep in my real space, not just a URL.
So I took a Chatbooks subscription (a monthly photo-book plan), and set out to give the projects I build through vibe coding and agent coding, with Claude, ChatGPT, Grok and Gemini, a printed counterpart. I composed the first book by hand. Over time the work moved into tools: ChatGPT helped make HTML book previews, and each book taught us something about trim, spreads, QR codes and how to review before printing. This page collects those learnings, and the same text works as a skill that agents follow.
The method on this page didn't start here. For a month before it, ChatGPT and I tried to turn the Sora archive, 2,719 generated clips, into something readable. The goal was never only a photo book: the same body of work was published and prototyped in several forms, each asking a different question. An editorial site asked what is the story; web readers asked what should be compared and in what order; a 5 × 5 proof asked what survives a page turn; cards and books asked what deserves to become physical. They are related forms of one publishing practice, not competing outcomes.
| Date | Form | What it taught us |
|---|---|---|
| 27 Aug | Sora to Print | A browser editor for square 1,500 px Chatbooks cards (title, short copy, QR code, safe area). Real pages, assembled one at a time. |
| 29 Aug | Sora Sketchbook / Act JRB20 | An editorial site that made the practice readable as a field record, not one canonical book. |
| 3 Sep | Picker and sorter | Keep, discard and bucket decisions with stable filenames: picking became separate from export. |
| 20 Sep | Editorial v1 | Five web books from 307 of 2,719 studies, grouped by project family. Legible, but it read like a catalogue. |
| 20 Sep | Editorial v2 | An independent cut of 38 scenes with a six-movement arc per book: an editorial spine, but still a plan. |
| 22 Sep | Editorial v3, The Long Arc | A 65-logical-page reader: stills first, pairs, grids, quiet text pages, mini-indexes. Paced, but not printer-ready. |
| 23 Sep | 5 × 5 Book Preview | A flip-book proof of spreads with a page map for swaps: back to the scale of a Chatbooks upload. |

What didn't work, honestly. The early print work produced real pages and taught the constraints (safe areas, page order, the gap between an attractive square on screen and a readable printed page), but the Sora Sketchbook never resolved into one finished printed edition. The archive was too broad, the first cuts too category-led, and even the best web reader still needed another round of selection and page-level composition. A 60-page plan (v2) is not an edited 60-page book; "logical pages" (v3) are not printer pages; and no sorter or manifest replaced looking at the images together.
How the books resolved it. Every Frame answered the broad archive the other way round: no editorial cut at all, every frame in date order, with permanent clip IDs and a way back to each video. All I Need Music applied the same system to a smaller, curated collection. The editorial readers remain what they were good at: telling a story on screen.
What carried forward into the rules below:
How we make 5 × 5 in Chatbooks from our own collections, learned on two books in Sep 2026:
/Users/briji/x sora jrb20 backup/sora video gallery/book/ (scripts/mosaic.mjs)/Users/briji/vibe-coding/pet-projects/all-in-need-music/book/ (build_sample.mjs)Both books are HTML pages rendered to PNG with Playwright. Content lives in data files, and layout lives in one script plus one stylesheet.


| Fact | Value | What we do about it |
|---|---|---|
| Book size | 5 × 5 in, 60 images max per book | Plan every book as exactly 60 images |
| Image 1 and image 60 | Single pages (right, then left); everything between pairs into spreads 2–3 … 58–59 | Design images 2–59 as spreads; use 1 and 60 for single-page content (intro, back) |
| Edge trim | Keeps the central ~95.5%: about 34 px of 1500 (0.11 in, 2.9 mm) per side, even on all sides, zoom-to-fill | Add bleed, and keep all text ≥ 24 CSS px (0.25 in) inside the trim |
| Cover / first-page tab | Chatbooks lays its own vertical date tab over the left edge, about 44 CSS px wide | Keep cover text ≥ 88 CSS px from the left; right-hand vertical titles read bottom-to-top, matching the tab |
| Page labels | Chatbooks' "Page N" counts image 1; our printed numbers start at image 2 | Expected; the index uses the printed numbers |
How we measured the trim: fit the Chatbooks preview screenshot against our own PNG (best crop match: 95.5%, ±6 px). Redo this with a calibration page if a new product or size is used.

deviceScaleFactor = 1500 / 480 gives 1500 × 1500 px.add_bleed.py.) For true full-bleed art, build the extra area in HTML instead (Every Frame bleed study: book/review/bleed-study/).01.png … 60.png in upload order, plus hardcover.png if needed. No spread PNGs; Chatbooks takes pages.project/book/print/) and list them as never-deploy in DEPLOY.md.


#0001–#2719 by date; music H-### / T-###). IDs get printed, so never renumber after printing. If the source must change, remap old → new IDs and spot-check.python3 -m http.server <port> --bind 0.0.0.0, then http://<mac-ip>:<port>/…). Show spreads as the printed book opens: image 1 alone on the right, pairs, image 60 alone on the left.



year · ID / title / composer or artist (+ tracklist on feature pages).flex:none); object-fit: cover squares.name ····· page H-012, with the ID in fixed lanes (letter / dash / digits) and dotted leaders instead of row rules. Show the year for same-title rows.briji-projects.pages.dev/?video=123, /v/123, finder at /v — All I Need Music: all-i-need-music.pages.dev/?album=T-053, /a/T-053, lookup at /a_redirects can't put placeholders into a query string. Use a 200 rewrite to a page that reads the path.AGENTS.md / DEPLOY.md: explicit approval, a log row, AGENT-LOG.md, and the checkmarked report.project/book/, with print/ inside).?v=<timestamp>.Curated by <agent> · Composed by <agent> · Steered by <owner> · <date>, set as small labelled blocks, centred. (Printed books only. Public web pages follow the public-attribution policy.)