Fonts
Text draws nothing without a font. There is no substitution and no error: the text run is simply absent while everything else renders. This is the single most common way a Clayzo document goes wrong, so it’s worth understanding. A text run finds its typeface one of two ways:
Embed fonts as assets. Put the file next to the document and declare it:
fontAssetId: "manrope". The uri resolves relative to the document on disk, so preview, export and packaging all find it.
Things to know:
- Use a permissively licensed face (an OFL family such as Manrope or Inter) that you can ship. A system font renders on your machine but can’t be embedded, and fails everywhere else.
- With a variable font,
fontWeightsets its weight: one file covers every weight on its weight axis, and a value outside the axis’s range is clamped to it. - A static font draws at its own weight. At a
fontWeightof 600 and above, a face lighter than semibold is given a synthetic bold; a bold face is drawn as it is. - A glyph the face lacks draws as a hollow box. Stay within the face’s character set.
- Text is kerned from the font. Complex scripts, right-to-left text and ligatures aren’t shaped: each character draws as one glyph, left to right. The
text.shaping_fallbackdiagnostic is normal and means exactly this.
Images
image node references the asset by id and fits it into a box. PNG, JPEG and WebP are supported. Declare the real pixel dimensions; the node’s size decides how it’s fitted.
In the browser
By default a player fetches each asset’suri as a URL relative to the page. When that isn’t right, pass loaders:
Uint8Array, or null when it has nothing. familyFontLoader is how a family-name run gets a typeface in the browser; return your best match rather than null, because null means no text.
The CanvasKit player reports what it couldn’t resolve:
Make it portable
A.clayzo bundle embeds the document with every font, image and audio file it uses, so no loader configuration is needed anywhere:

