The component library

Prototemplate's live component library: every instrument mounted on its page with controls, its import path, its props and its reduced-motion still.

Updated

These pages mount the real components of the hub: the dithered fields, the light fields, the glyph rain, the doubled line, the isometric kit, the globes, the locale chips, the moving text, the marks and the heads. Every plate is the component itself, running, with controls for its props. Each section gives the import path, the props, when to use the component and what it does under reduced motion. The plate below is the studio field, the dithered material of the landing hero, with its ten presets.

The pages

The pages sit in ten groups, as the sidebar shows them: the instruments first (fields, the dither lab, drawings, 2D and 3D), then the pieces a reader handles (interactables, language and motion, marks and heads), then what the system makes and where it puts it (artifacts, page layouts, pages). Each card names the components its page mounts.

Fields

Dither lab

Drawings

2D and 3D

Interactables

Language and motion

Marks and heads

Artifacts

Page layouts

Pages

Every component

ComponentFileClientPage
createDitherLoop and the field factoriessrc/lib/dither.tsyesDither
BAYER_8, BAYER4src/lib/dither.ts, src/app/d/toolchain/diagrams/DitheredMark.tsxnoDither
StudioField, createStudioFieldsrc/components/shared/StudioField.tsx, src/lib/studio-field.tsyesDither
createHorizonFieldsrc/lib/horizon-field.tsyesFields
PrismaticField, createPrismaticFieldsrc/components/shared/PrismaticField.tsx, src/lib/prismatic-field.tsyesFields
createGlyphFieldsrc/lib/glyph-field.tsyesGlyph field
createInkFieldsrc/app/d/glyph-rain/sections/band/inkField.tsyesGlyph field
DoubledLinesrc/components/shared/diagrams/DoubledLine.tsxnoLines
TranslationFlowsrc/app/d/toolchain/diagrams/TranslationFlow.tsxyesLines
The isometric kit, IsoFramesrc/app/d/toolchain/diagrams/iso.ts, IsoFrame.tsxnoIsometric
EdgeGlobesrc/app/d/toolchain/diagrams/EdgeGlobe.tsxyesGlobes
globe()src/lib/dither.tsyesGlobes
GlobeFramesrc/components/blog-globe/GlobeFrame.tsxyesGlobes
LocaleTagsrc/app/d/toolchain/components/LocaleTag.tsxnoLanguage
LocaleFlagsrc/components/shared/LocaleFlag.tsxnoLanguage
The language diagramssrc/app/d/toolchain/diagrams/lang/yesLanguage
RevealSeamsrc/app/d/toolchain/sections/RevealSeam.tsxyesMotion
EverySentencesrc/components/shared/EverySentence.tsxyesMotion
MARKS, GLOBE_CONCEPTSsrc/lib/marks.ts, public/marks/noMarks
GtWord, gtText, GtMarksrc/components/viewer/GtWord.tsx, src/components/viewer/GtMark.tsxnoMarks
DitheredMarksrc/app/d/toolchain/diagrams/DitheredMark.tsxno, the host animatesMarks
BadgeCycle, badgeMarksrc/components/viewer/BadgeCycle.tsxnoHeads
BookHeadsrc/components/viewer/BookView.tsxyesHeads

Client means the module is a 'use client' component or a canvas engine that needs the DOM. The rest render on the server and ship no JavaScript of their own. Several components live in src/app/d/toolchain, the direction that the later directions fork. The radius, type and oxlint gates skip that folder, and the site imports the components from there.

The lifecycle every instrument keeps

  1. Mount late. A canvas or WebGL engine starts when its plate comes within 180 pixels of the viewport, through one IntersectionObserver that disconnects after it fires.
  2. Pause when unseen. From then on the engine pauses itself when it leaves the viewport and when the tab is hidden.
  3. Draw one still under reduced motion. With prefers-reduced-motion: reduce an engine draws one designed frame and schedules no animation frames. A Still control on a plate shows that frame without changing the system setting.
  4. Release on unmount. destroy() disconnects every observer and listener. The WebGL contexts of the studio, horizon and prismatic fields persist for the session, one per engine, because a context per mount exhausts the browser's budget.
  5. Follow the theme. The theme is html[data-theme]. An engine reads its ink from a custom property and reads it again when the attribute changes; an SVG drawing reads currentColor and the tokens.

The fields tuned to an ink ground, the studio, horizon, prismatic and ink fields, sit on an ink plate in both themes. Everything else follows the docs theme.

The numbers behind it

The browser keeps a fixed budget of live WebGL contexts, and a page that runs past it gets a black canvas and no error in the console. The lifecycle above keeps every page inside that budget, and these numbers set it.

NumberValueWhat it decidesWhere it is held
Live WebGL contexts in Chromeabout 16Past the budget getContext returns null and a field draws black. On 2026-07-29 a direction's shader rendered on a fresh load and was black after 12 client-side switches between directions.One module-level engine per engine type, each drawing into one offscreen GL canvas and blitting into every mount's 2D canvas: src/lib/studio-field.ts, horizon-field.ts, prismatic-field.ts
Contexts per engine module1 for the sessionThe context survives a mount's destroy(), so route changes never create another.The same three modules
Contexts a probe may create0A probe that calls getContext on the page's canvases to count them creates contexts and exhausts the budget it measures.Wrap getContext before the page's scripts run, or screenshot after real navigation
Contexts a component may leak0A component that owns its context deletes its buffer, its program and both shaders, then calls WEBGL_lose_context.loseContext() on unmount. A footer mark that skipped this leaked one context per mount (2026-08-08).The component's own cleanup
Arm margin180 pxA lazy plate mounts its engine when it first comes within 180 px of the viewport. A plate the reader never scrolls near never starts its engine.rootMargin: '180px' in src/components/docs/demos/hooks.ts
Live previews on one pagestills until hover22 live iframes running shaders made the presenter wall lag (2026-07-31). A gallery that froze each frame 2.8 s after load still held 12 live pages, 4 WebGL contexts and about 1.3 GB of renderer memory, and stayed busy about 950 ms of every second at rest (2026-10-08). A frozen same-origin frame still hydrates, composites and runs its CSS animations on the parent's main thread.src/app/present/viewer/LazyFrame.tsx shows a thumbnail and mounts the live page only while a mouse is over the card
An off-screen live stageabout 110 ms of every secondThe presenter's live prototype stage cost that much through the slides before it, so it is frozen whenever no part of its section is on screen.The rAF gate in src/app/layout.tsx, driven by postMessage({ type: 'gt:freeze', frozen })

The rAF gate wraps requestAnimationFrame only, so a loop on setInterval or setTimeout keeps running inside a frozen preview. Canvas and WebGL engines explains each of these with the shared canvas that only grows, the readback canvases, zoom and the quality governor.

Mounting a component in a docs page

A docs page imports a component at the top of its MDX file and renders it as JSX. The page and its MDX are server components, so a component with state, a canvas or GSAP must be a 'use client' module. fumadocs-mdx imports every docs page into the one /docs/[[...slug]] route, so a plate imported directly would put its code and its stylesheets into every docs page's first load. The pages therefore import their plates from src/components/docs/demos/lazy.tsx, which wraps each one in next/dynamic: the server still renders the plate, and its chunk and CSS load only on the page that shows it.

import { StudioFieldDemo } from '@/components/docs/demos/lazy';

The plate runs the studio field with its ten presets.

<StudioFieldDemo />

Put a sentence beside every plate that says what it shows. The Markdown twin of the page and /llms-full.txt drop the import and keep the plate as a bare tag, such as <StudioFieldDemo />, so the sentence is what a reader of the twin gets.

The plates on these pages are built from one small kit in src/components/docs/demos/:

PartWhat it does
PlateThe card: the stage, the entry point's name in its corner, and the controls row. night pins the stage to ink, lazy mounts the children when the plate nears the viewport, fit takes the content's height, and moves adds the reduced-motion note.
Choice, Range, Toggle, ActionThe controls: a radio group drawn as a segmented row, a range with its value, a pressed button and a plain button.
useArmed, useReducedMotion, useTheme, readToken, useReplayThe lifecycle hooks behind the list above.
lazy.tsxEvery plate wrapped in next/dynamic, with a fallback at about the plate's height for a client navigation. A new plate gets its line here.

The build log

The handbook's Libraries document indexes the same instruments and ends with the build log, where every library runs with its plates and API snippets.

On this page