Guide
Getting started
Install blotter.ts, put your first text on a canvas, and learn what happens underneath.
Why Blotter #
CSS runs out of tricks for text quite quickly. You get shadows, gradients, a clip-path if you are feeling brave, and then you are exporting PNGs. Blotter takes a different route: it hands each word on your page to a fragment shader and draws it, every frame, on the GPU. The text stays typographic. Your font, your size, your weight. Only the pixels are up for negotiation.
The trade is that what comes back is a canvas, not a DOM text node. Nobody
can select it and screen readers will not see it, so keep the real text nearby. Blotter is
for titles, headings, logotypes and words used as graphics. It is the wrong tool for a
paragraph.
Install #
Blotter renders through three and leaves it to you to install, so the two go
in together.
npm install blotter.ts three
The package is an ES module with types. Everything comes from two entry points: the core
classes from blotter.ts, and the ready-made effects from
blotter.ts/materials. Import what you use; the rest is shaken out of your
bundle.
import { Blotter, Material, Text } from "blotter.ts";
import { ChannelSplitMaterial } from "blotter.ts/materials"; Your first text #
A Blotter needs two things: at least one Text, and a Material
that says how to draw it. The plain Material applies no effect at all, which
makes it the right place to start: if the word below looks like the word you typed, the
pipeline is working.
import { Blotter, Material, Text } from "blotter.ts";
const text = new Text("observation", {
family: "'Castoro', serif",
size: 64,
fill: "#171717",
paddingLeft: 20,
paddingRight: 20,
});
// The plain Material draws the text exactly as described.
const blotter = new Blotter(new Material(), { texts: text });
blotter.forText(text)?.appendTo(output);
await blotter.ready; What just happened #
Four objects took part, and you will meet them again on every page of this guide.
- A Text is a string plus the styles to draw it with. Blotter measures it and rasterises it once, at construction.
- A Material is a fragment shader with named uniforms. Every text that shares a material is drawn by the same program.
-
The Blotter packs its texts into one texture atlas, renders the whole atlas
through the material in a single WebGL draw call, and keeps doing so on
requestAnimationFrame. There is one WebGL context for the entire page, however many Blotters you create. -
A RenderScope is what you get back from
blotter.forText(text): a per-textcanvasthat copies its own rectangle out of the shared buffer each frame, plus a place to set uniforms for this text alone.
blotter.ready resolves after the first build. Nothing needs it to be awaited
before appending a canvas, but anything that reads per-text uniforms or the rendered
imageData should wait for it.
Fonts come first #
Because a text is rasterised the moment it is constructed, the font has to be there already. Build a Blotter while a web font is still downloading and you will get the fallback face baked into the atlas. The fix is one line: ask the font loading API for the exact face, weight and size you are about to use.
const text = new Text("observation", {
family: "'Castoro', serif",
size: 64,
});
// Load the exact face, weight and size Blotter is about to draw with.
await document.fonts.load('400 64px "Castoro"', text.value);
await document.fonts.ready;
const blotter = new Blotter(material, { texts: text }); Where next #
Texts and uniforms covers styling, changing a text after the fact, and turning the dials on a running effect. The materials pages show the five bundled effects with live controls. When none of them is quite right, custom materials walks through writing your own, and the API reference lists everything the package exports.