General Translation's 1-bit material and its artifact pictures.
node scripts/install-skills.mjs gt-dither --project <dir>Run it from a Prototemplate checkout. It links skills/gt-dither into the project’s .claude/skills and .agents/skills; --copy vendors the folder instead, and --dry-run prints each step first.
General Translation (GT) builds the full stack for localization: i18n libraries, context-aware translation APIs, a CDN that serves translations and Locadex, its AI agent (BRAND.md sections 1 and 9). Its product monorepo is gt-cloud (github.com/generaltranslation/gt-cloud): apps/landing serves generaltranslation.com and apps/dashboard holds the sign-in and onboarding pages, a form plate on the left over a dithered field on the right. Prototemplate (github.com/Kevin-Liu-01/Prototemplate) is Kevin Liu's hub for GT design work: the brand deck, the docs, and the plate port, a copy of the dashboard's sign-in and onboarding gallery under src/components/plate, served at /d/production/{signin,onboarding,consent,device,cli}.
GT draws texture with one device: an ordered Bayer dither that prints whole square cells in one ink. The same screen runs the procedural fields on the landing, the dashboard and Prototemplate, and the artifact pictures, which are photographs and scans of objects screened at 1 CSS px cells to the Blue Marble standard. This skill holds the rules, the engines, the commands and the review standard for both.
Paths are relative to a Prototemplate checkout ($PROTOTEMPLATE) unless they start with $GT_CLOUD, a gt-cloud checkout. Engine detail is in references/engines.md; the picture roster, the per-surface constants and the full lint list are in references/pictures.md.
src/lib/dither.ts, module header).| screen | values | where | | --- | --- | --- | | 4x4 | rows 0 8 2 10, 12 4 14 6, 3 11 1 9, 15 7 13 5 | bayer4 in src/lib/studio-field.ts, preset 01 and most of the family | | 8x8 | BAYER_8, built by M(2n) = [[4M, 4M+2], [4M+3, 4M+1]] | src/lib/dither.ts, the cutter, the deck engine, presets 02, 10 and bayerSphere | | 2x2 | four thresholds | preset 07 bayer-chunk only |
(m + 0.5) / 64.DitheredMark, DESIGN.md section 6; see gt-isometric).image-rendering: pixelated (applyStyles in ditherToCanvas). The CPU engine never renders at devicePixelRatio: a retina cell is subpixel and the screen collapses into flat gray.shape-rendering: crispEdges.rotate() windows fragile (DESIGN.md section 6).scale is the CSS px width of one cell, and eight times it is the grain the eye reads. The artifact pictures use 1 for fidelity, and the sign-in globe and the deck's ramp use 2. At 3 and 4 the screen reads as halftone, and 3 is the default for a full-bleed procedural field. At 8 or more it reads as pixel art.rampField in src/components/plate/brand/FieldStack.tsx), so the dither drops whole cells across the ramp. A fading CSS mask over a picture prints its cells at partial alpha, so picture layers take a hard cut at the plate's edge and do their dissolve in the field.TRANSITION_RULES in src/app/craft/libraries.ts):phase option), so the first frame of a transition is the last frame of the state before it;mixFields);RESOLVE_MS and STEP_MS in src/app/craft/TransitionDemo.tsx, STEP_MS in the plate's FieldStack.tsx; Kevin, 2026-09-29: "make the dither transitions 2x faster").deck/shots/OPENERS.md).| engine | file | use it for |
|---|---|---|
| CPU dither | src/lib/dither.ts | any scalar field fn(u, v, t) to 0..1 through the 8x8 screen: field factories (radialBurst, globe, streakBands, gradientRamp, makeGlyphField), combinators (multiplyFields, maxFields, mixFields, mapField), pictures, transitions, the deck ramp (redrawDithers) |
| GPU studio field | src/lib/studio-field.ts | the Bayer family of animated materials: createStudioField(canvas, { preset, dpr, speed, params }), BAYER_PRESETS, BAYER_DEFAULT_ID = '02' |
| React wrapper | src/components/shared/StudioField.tsx | a canvas that mounts one studio field and destroys it on unmount |
| picture sampler | src/components/plate/lib/picture-field.ts | a tone grid placed by cover, disc or region, read at the loop's cells |
createDitherLoop(canvas, fn, opts) returns render, start, stop, setField, setOptions and destroy. Defaults: scale 3, fps 30, pauseOffscreen true, gamma 1, bias 0.createStudioField returns setParams, pause, resume, renderStatic and destroy, or null when WebGL is unavailable. The canvas then stays transparent and the parent's own ground shows.#2f5ce0, #5f86f2, #9db9ff, #cfe0ff), white only in 10:| id | name | preset | what moves | | --- | --- | --- | --- | | 01 | bayer-flow | bayer | the Glyphfield original: 4x4 over flow clouds at 2 to 10 device px cells | | 02 | bayer-8x8 | bayer8 | the same flow through the 8x8 at 1 to 4 device px cells; the default and the hero's material | | 03 | bayer-contour | bayerContour | elevation bands drifting downslope | | 04 | bayer-radial | bayerRadial | two glows at the flanks, the centre column ink | | 05 | bayer-sweep | bayerSweep | long diagonal bands with a slow churn | | 06 | bayer-waves | bayerWaves | two interfering wave systems, the slowest clock | | 07 | bayer-chunk | bayerChunk | the 2x2 at 8 to 22 px poster cells | | 08 | bayer-pulse | bayerPulse | the flow breathing on a 16 s clock | | 09 | bayer-ink | bayerInk | sparse blue on ink, no bright chip | | 10 | bayer-hot | bayerHot | heat cores lifting crests to white through the 8x8 |
bayerSphere sits outside the roster: a lit sphere through the 8x8, the landing's report card globe and Prototemplate's /try figure.
prefers-reduced-motion, release everything the instance owns in destroy(), and re-resolve ink on a data-theme flip. The studio field draws one ink set in both themes and leaves the light theme to the page's CSS filter (section 3).destroy() keeps the shared context by design. Switch a preset by remounting a keyed canvas (src/components/shared/HeroFieldSwitcher.tsx).packages/ui's on PR #4977, listed in references/engines.md with the gotchas). Change a preset in every copy in the same round, or the app's material drifts from the hero's.createStudioField.shared/HeroField.tsx on canvas.tc-hero-field.tch-field, and three files under $GT_CLOUD/apps/landing/src/components/landing/ set its composite:shell/engine.css: absolute, full bleed, mix-blend-mode: screen, inside an isolated dark cell;home/sections/hero-terminal.css: opacity 0.55 and a seven-stop horizontal mask, 0.72 at the edges to 0.12 in the centre behind the window;home/v0-pages.css, light theme: filter: invert(1) hue-rotate(180deg) brightness(1.07) saturate(1.15), so the clouds print pale blue on paper.FieldGround (ground for page grounds, band inside a block) and DitherBand (an empty state with its copy on a paper plate in the lower left). They live in $GT_CLOUD/apps/dashboard/src/components/brand/ on PR #4977 and in src/components/plate/brand/. The composite is the hero's, measured:.brand-field-host is isolation: isolate, pointer-events: none;.brand-field is opacity 0.55;ground masks the field to nothing left of --plate-edge, then ramps to full over 600 px; under md it is a faint top band gone by mid-page;band ramps from 0.12 to full across the block;glyph-field) keeps its glyphs as anti-aliased type at the page's pixel ratio. Its depth is glyph size and a quantized alpha ramp, and every tier is solid ink (TIER_COVER [1, 1, 1] in src/lib/glyph-field.ts and $GT_CLOUD/packages/ui/src/lib/glyph-field.ts), because a dither pattern on moving glyphs shimmers with every step. Kevin removed the rain's far-tier dither for that reason (commit b59f7d4, 2026-08-06: "the dither is making it flicker every time it moves"). A later pass that redrew the rain as enlarged two-tone pixel type was reverted on his word (2026-09-28: "make the glyphs look like how they did before, and dont make them dithered").GLOBE_SCALE 2 (the rain left it on 2026-09-29), and the onboarding, consent, device and CLI pages draw artifact pictures at PICTURE_SCALE 1 ($GT_CLOUD/apps/dashboard/src/components/brand/FieldStack.tsx). The dashboard field's file map is gt-cloud's artifact-pictures skill (PR #5133).aria-hidden, no pointer capture, and a readable page without WebGL.glyphfield-dither-gradient, is a moving wave distorted by layered noise under a 4x4 Bayer threshold with three palette colors; its grain sets the screen-space cell size.createStudioField;window.glyphfield.studio and discover the API at /llms.txt and /api/agent. The headless export recipe is in gt-graphics; the landing code map is gt-cloud's .agents/skills/glyphfield.An artifact picture is a photograph or scan of an object, artwork or place, screened through the house dither. They appear on the plate port's field (src/components/plate), the brand deck's mood slides (deck/), the transition demo on /docs (src/app/craft) and gt-cloud's dashboard sign-in and onboarding field. Procedural fields (the sign-in globe, the ramp), marks, glyph fields, the deck's shader openers and blog covers are not artifact pictures.
Choosing a picture:
NOTE_MAX_CHARS) that states one fact about the object, and the credit in italic (Kevin, 2026-10-01). Kevin asked for notes of two lines at most, and the note never mentions the company (2026-09-30: "dont write the corny stuff about gt relating").deck/shots/OPENERS.md). The gt-deck skill owns that format.The tone grid (scripts/mood-tone/standard.json under tone and file):
red or gray), the polarity (invert puts the subject lit on a black ground) and the kind.scene lands on the Blue Marble's mean tone 0.361 and standard deviation 0.342 over the region of the grid that is shown.marks (writing, engraving, lines on a plain ground) put the ground at black with an Otsu threshold and the 90th percentile of the marks at white.The screen (standard.json under screen):
(m + 0.5) / 64;#070707; light theme: #070707 cells at 0.7 over white.The windows the lint holds each grid's shown region to:
| kind | mean | std | at white (250 and up) | at the floor (10 and under) |
|---|---|---|---|---|
| scene | 0.30 to 0.42 | 0.28 to 0.44 | at least 2% | at least 5% |
| marks | at most 0.35 | any | at least 0.5% | at least 50% |
The constants on each surface, the picture roster and the crop kinds are in references/pictures.md.
Requires Node 20 or later and Python 3 with Pillow. From the Prototemplate root:
pnpm mood-tone <sources dir> # the deck set into deck/shots/tone
pnpm mood-tone <sources dir> --set plate # the plate set into public/brand/mood
pnpm mood-tone <sources dir> --preview <dir> # also writes preview-{name}.png, the 1 px screen
pnpm mood-tone <sources dir> --check # cuts into a temp folder, fails unless byte-identical
pnpm mood-tone <sources dir> --out <dir> # writes the grids and manifest elsewhereIn gt-cloud (PR #5133, branch k/artifact-picture-standard, open on 2026-10-05): pnpm --dir apps/dashboard mood-tone <sources dir> [preview dir], or the sources directory in MOOD_SOURCES_DIR.
On Kevin's machine the sources are in ~/gt/artifact-picture-sources with a SHA256SUMS file. pnpm mood-tone ~/gt/artifact-picture-sources --check and the same with --set plate passed on 2026-10-05.
mood-tone.mjs holds the recipes and SOURCES (each file's origin URL and sha256). It refuses a source whose sha256 differs, then sends the recipes and the standard to mood_tone.py, which writes mood-{name}.jpg and prints one manifest entry per picture. The wrapper writes manifest.json and prints each picture's levels, region stats, text lines and bytes.--check with the sources proves the entry came from the recipe. Never edit a grid or a manifest by hand. In gt-cloud the reviewer reruns the wrapper and checks that git status shows no change.name, source, the size and cap, crop, channel, invert, kind, writing and placement. It holds no tone numbers. If no crop, channel, polarity, kind or placement puts a scene in its window, the picture does not meet the standard; tone settings are never added to make it pass.SOURCES in mood-tone.mjs and its row (file, origin, pixels, sha256) to scripts/mood-tone/README.md.DECK or PLATE. Spread COVER for a cover. Use kind: 'marks' for writing or engraving on a plain ground and 'scene' otherwise. Declare writing. A deck placement is a cover at focus 0.5, 0.5. A plate recipe is gt-cloud's too: add the same recipe to PICTURES and the source to SOURCES in $GT_CLOUD/apps/dashboard/scripts/mood-tone/mood-tone.mjs, and the source row to that folder's README.--preview and look at preview-{name}.png. Adjust the crop, channel, polarity, kind or placement until the subject sits where it should and the stats meet the window.public/brand/mood holds the same bytes in each. The markup, the bookkeeping and the caption rules are in references/pictures.md.manifest.json, the recipe, the README row and the slide or registry entry together. If the manifest shows textLines above 0, say so in the commit or pull request so the reviewer checks the writing against section 5.To remove a picture, delete its recipe, grid and slide or registry entry, run the wrapper so the manifest drops it, and move its pages to another picture, in both repositories.
| repository | command | runs in |
|---|---|---|
| Prototemplate | node scripts/lint-pictures.mjs (pnpm lint:pictures), tests pnpm test:pictures | pnpm build before next build, pnpm lint:all |
| gt-cloud (PR #5133) | node scripts/check-artifact-pictures.mjs (pnpm check:artifact-pictures), tests pnpm exec vitest run scripts/__tests__ | pnpm lint |
Prototemplate's lint fails when a grid, its manifest entry, the registry or a slide disagree, when a grid or its stats are off the standard, when a credit is missing, when a retired name appears, when a screen constant is off on any surface, and when standard.json differs from its pin. gt-cloud's applies the same rules to its one manifest and registry, and also reads scene 1's makeLoop call. The full list, and what the lint cannot see, are in references/pictures.md.
standard.json and mood_tone.py are byte-identical in Prototemplate and gt-cloud, and STANDARD_SHA256 pins them in both lints. A change edits both repositories in one round and recuts every grid. The steps and the current pin are in references/pictures.md under "Changing the standard".
dictionary, johnson, oed-volumes, oxford and oed never return as picture names, files, registry keys, mood-{name} tokens or string literals in picture code. They are listed in standard.json under writing.retired; references/pictures.md under "Retired names" says what each was and why it went.
destroy(), and the page reads without WebGL. A CPU loop re-reads its ink on a theme flip (data-theme in Prototemplate, the .dark class in gt-cloud); the studio field takes the light theme from the page's CSS filter.SOURCES, and a credit shown with it.--check passes with the sources, and the page matches preview-{name}.png.node scripts/lint-pictures.mjs passes, and for any plate change gt-cloud's node scripts/check-artifact-pictures.mjs passes on PR #5133's branch (or main once it merges).GT: gt-graphics (covers, blog graphics, the Glyphfield headless export), gt-aesthetic, gt-isometric (DitheredMark), gt-deck (mood slides and openers), gt-landing-pages, gt-lints. Wiki: create-graphics, design-engineering-polish, agent-browser. gt-cloud's own skills under .agents/skills: artifact-pictures (PR #5133) maps the dashboard field's files, glyphfield maps Glyphfield and the landing's studio field, and gt-landing lists the landing's shared pieces.
k/dashboard-shell-ia (PR #4977) FieldGround.tsx, DitherBand.tsx and brand-tokens.css; on k/artifact-picture-standard (PR #5133) .agents/skills/artifact-pictures/SKILL.md and scripts/check-artifact-pictures.mjs.The folder skills/gt-dither as agents fetch it. Each file opens raw.
SKILL.mdreferences/engines.mdreferences/pictures.md