field instrument 001 · paper shaders

Atlas of
Unstable Matter

One specimen. Three irreversible acts. Expose it to light, give it a name, then pull a numbered proof.

plate 01light / time

I

Exposure Chamber

Hold light against the latent specimen. Aperture determines which mineral color survives the exposure.

latent plate · ready for exposure

Why this act: God Rays becomes an exposure tool; animation records a decision instead of decorating a background.

Specimen silhouetteunnamedawaiting exposure
plate 02heat / identity

II

Annealing Table

Name the exposed form, then commit it to heat. Editing is cheap; annealing is not.

complete exposure first

Why this act: Heatmap turns a personal alpha mask into a material event, not an interchangeable preset.

Specimen silhouetteunnamednot annealed
plate 03ink / proof

III

Build the proof in process order. Cyan, magenta, yellow, then key black. No skipped plates.

Why this act: Halftone CMYK fixes every upstream choice into one artifact. Change the exposure or name and the proof must change.

field notesdecisions / limits

Research → decision index

The official gallery already proves breadth. This page asks a narrower question: can three shaders behave like one instrument?

Official gallery
Catalogs and parameter panels are solved → one causal artifact, no raw inspector.
Heatmap / alpha masks
Personal marks carry meaning → the visitor’s word becomes material and persists.
Performance reports
Offscreen pause retains GPU allocation → one active mount, explicit context loss.
Print-studio prior art
Upload and before/after are familiar → ordered plate-making replaces comparison UI.
Rejected finalists

Pocket Biome lacked artifact continuity. Orbit Choir added sensor/audio permission cost. Liquid Metal’s synchronous preprocessing and context churn weakened the production story. A photo studio duplicated strong existing work.

Evaluation and limits

Required gates: exactly three dependent phases; one active shader mount; mobile canvas ≤750k pixels; reduced motion uses deterministic frames; WebGL failure reveals honest posters; no runtime CDN requests. Visual review scores signature image, discovery, continuity, responsive composition, motion authorship, and technical proof.

Sources and license

Built with @paper-design/shaders 0.0.77 by Paper, Apache-2.0. See the bundled LICENSE and NOTICE. Inspiration research included the official gallery, Aperture Shader Studio, FoldGradient, Pixel, and an independent Paper Heatmap port.

Making three shaders behave like one instrument

Engineering notes · 8 min read

Making three shaders behave like one instrument

A shader demo gets interesting when its effects inherit state and the runtime has to release what it creates.

Paper's shader library already has a strong gallery. It shows what each effect can do, exposes the parameters, and gives developers code they can reuse. A second gallery would have repeated work that was already done well.

We built Atlas of Unstable Matter around a different question: what happens when several shaders have to share one object, one history, and one runtime budget?

The page turns a single specimen into an exposed plate, an annealed mark, and a numbered CMYK proof. This post explains the decisions that made those stages feel connected, the WebGL constraints that changed the design, and the tests that kept a visual experiment from becoming a fragile demo.

The specimen gave every shader the same job

The first concepts treated God Rays, Heatmap, and Halftone CMYK as separate demonstrations. They looked good in isolation and fell apart as a page. A visitor could change one effect without that choice mattering anywhere else.

The fix was a small state machine:

latent → exposed → annealed → printed

Exposure duration and aperture choose a palette. The visitor's word is written into the same asymmetric specimen. Annealing converts that combined mask into a thermal field. The press inherits the image, palette, exposure value, mark, incision, dot, and edition number.

The controls follow the same rule. Later acts stay locked until the specimen has the state they require. A visitor can inspect the next station, but cannot produce a valid proof by skipping the work upstream.

That dependency did more for the experience than another dozen shader parameters would have. Each effect now has a reason to exist.

Removing the flashiest option improved the system

Liquid Metal was one of the early finalists. It is immediately impressive, which made it an easy choice during ideation. Its preprocessing and likely context churn made it a poor fit for a mobile sequence with several live graphics.

We cut it.

God Rays became the exposure source. Heatmap became the material transformation. Halftone CMYK became the output process. Those three effects have different visual grammars, but each can operate on the same specimen without pretending that an unrelated animation is part of the story.

The cut also clarified the mobile interaction. Act I uses a hold gesture and an aperture control rather than spatial dragging that could trap scrolling. Act II asks for a short word. Act III uses an ordered set of physical-looking plate buttons. The mechanics change because the job changes.

WebGL lifecycle became a product constraint

A page can remove a canvas and still leave its graphics context allocated inside the browser. Three sections mounting and unmounting during a long scroll can quietly accumulate contexts, especially when retries and failure recovery create new mounts.

The atlas allows one active ShaderMount at a time. An intersection arbiter chooses the dominant act. Before another shader mounts, the current mount disposes its resources and requests explicit context loss through WEBGL_lose_context when the browser exposes that extension.

Mounting also runs as an ownership-scoped transaction. Every attempt receives a generation number. After asynchronous work, it checks that it still owns the scene before changing shared state. A superseded attempt releases only its own canvas and context. A 2.5 second readiness timeout falls back to the authored poster instead of leaving a blank stage.

Some browsers do not expose explicit context loss. In that case, the application permits at most one context creation per act. Reopening an exhausted act stays in poster mode and asks for a reload. That limit is conservative on purpose. A visual extra should not degrade the rest of the page.

Heatmap processing moved off the main thread

The annealing step needs more than a word. It rasterizes the specimen silhouette, the branch-like incision, the dot, and the visitor's mark into a source mask. Several blurred distance bands are then packed into image channels for the Heatmap shader.

The first implementation ran the library's preprocessing path on the main thread. It worked, but an explicit commit could pause interaction for hundreds of milliseconds. Automatic prewarming also created races between scrolling, committing a mark, and mounting the next scene.

The released version performs that work in a dedicated OffscreenCanvas worker. Its processing surface is capped at 624,400 pixels. The worker uses summed-area tables to produce three blur bands, then transfers the packed PNG back to the page. Processing measured 69 to 229 ms in repeated local release runs and 187 to 431 ms during the final live run. The verifier observed no main-thread long task during that processing window.

The worker starts only when the visitor commits the anneal action. Resetting the specimen increments the processing generation, so a late worker result cannot advance a specimen that no longer exists.

There is still a limit here. Browsers without workers or OffscreenCanvas receive the designed poster state. We chose a truthful fallback over a second, slower code path that would be harder to reason about and test.

The proof needed two layers

The first CMYK proof covered the paper with dense dots and grain. It had print character, but the title, border, exposure notation, and edition metadata were hard to read on a phone. It also dropped the incision and dot that made the specimen recognizable.

The final proof keeps a clean source plate underneath a restrained shader layer. The source preserves typography, border, metadata, and the full specimen. The live Halftone CMYK canvas uses screen blending to add plate texture and misregistration without turning the paper into noise.

Before the fourth plate, the source remains faint and grayscale. Cyan, magenta, yellow, and key each produce a distinct rendered state. After the key plate lands, the full-color source resolves underneath the halftone layer.

This was a useful reminder: an effect can be technically correct and still be the wrong compositor. The final image matters more than shader purity.

The verifier tests the story, not only the route

The first acceptance run failed because /unstable-matter/ did not exist. That gave the project a clean baseline before implementation.

The finished verifier drives the complete journey at 320×568, 390×844, and 1440×1000. It checks that the mobile plate and exposure control appear in the first viewport, interactive targets meet the 44 pixel minimum, and later actions remain unavailable until their prerequisites are complete.

It also checks the parts that visual smoke tests tend to miss:

  • pointer cancellation restores a latent specimen, and a second pointer cannot finish the first pointer's hold;
  • a keyboard can complete the entire sequence;
  • reduced-motion changes take effect while a shader is mounted;
  • orientation changes do not create overflow or a second canvas;
  • stale Heatmap work cannot advance a reset specimen;
  • failed mount construction cleans up and can retry;
  • missing WEBGL_lose_context support stays within the three-context lifetime bound;
  • 10 complete expose, anneal, print, and reset cycles end with one active context;
  • the three palettes and different marks produce different rendered pixels;
  • all four printing plates produce distinct proof states.

The main bundle is 37,321 bytes gzipped. Active canvases measured between 149,776 and 667,368 pixels in the final release suite. Local release verification passed twice before deployment, and the same journey passed against the canonical URL.

What remains unproven

The automated suite uses real Chrome and mobile viewport dimensions, but it is not a physical-phone thermal test. A five-minute run on an older phone could still reveal power use, driver behavior, or memory pressure that desktop Chrome emulation will not.

The worker blur is also specialized for this specimen pipeline. It is not a general replacement for Paper's Heatmap preprocessing, and the application deliberately falls back when the required browser primitives are missing.

Those boundaries are part of the result. The atlas treats GPU allocation, cancellation, motion preferences, and failure states as visible design inputs. The shaders become more convincing because the surrounding system knows when to stop rendering them.

Build the dependency before polishing the effect

The best decision in this project was giving every shader responsibility for the same artifact. That forced narrative continuity, exposed lifecycle costs, and made downstream verification possible.

Start with the state that must survive. Give each effect one transformation of that state. Then measure the browser behavior that the composition creates. The visual polish has something solid to attach to after those pieces work.