Skip to content

Rendering Animations

Tegaki handles text layout, line wrapping, and animation playback. See the framework guides for framework-specific setup.

import { TegakiRenderer } from 'tegaki';
import bundle from 'tegaki/fonts/caveat';

<TegakiRenderer
  font={bundle}
  time={{ mode: 'uncontrolled', speed: 1, loop: true }}
  style={{ fontSize: 48 }}
>
  Hello World
</TegakiRenderer>

Tegaki reads its typography from the container’s own CSS. Set font-size, line-height, color, and letter-spacing however you normally would (inline style, a class, a CSS variable) and the renderer picks them up, re-measuring when they change. letter-spacing is applied to the animated strokes just as the browser would space static text, so spacing, line wrapping, and the drawn glyphs stay in sync. line-height: normal gives the font’s own line spacing: it differs from font to font (Tangerine sets lines 1em apart, Amiri 1.76em) and is measured from the browser, so the strokes land where the text would. Most pages inherit a fixed line-height (Tailwind’s base styles set 1.5), which is loose for handwriting fonts with small letters; set normal or a smaller ratio on the renderer to tighten it.

<TegakiRenderer font={bundle} style={{ fontSize: 48, letterSpacing: '0.1em', color: '#3b82f6' }}>
  Hello World
</TegakiRenderer>

The component manages its own animation clock:

time: { mode: 'uncontrolled', speed: 2, loop: true }

Options:

  • speed: playback speed multiplier (default 1)
  • loop: restart when animation completes (default false)
  • playing: pause/resume (default true)
  • catchUp: speed up when buffered text is ahead (for streaming)

You provide the current time value directly (useful for syncing with external state):

time: 2.5 // seconds
// or
time: { mode: 'controlled', value: 2.5 }

Animation is driven purely by CSS (best performance, no JavaScript animation loop):

time: 'css'
// or
time: { mode: 'css' }

The bundles record how wide the pen draws at each point. pressure (0 to 1, default 1) sets how much of that the ink follows: 0 draws each stroke at one even width.

pressure: 0.5

Glows, tapered ends, gradients and a hand that varies each letter are plugins, and all frameworks take them the same way, as a plugins list. They run in the order listed.

import { glowPlugin, strokeGradientPlugin, taperPlugin, variationPlugin } from 'tegaki/core';

plugins: [
  taperPlugin({ startLength: 0.15, endLength: 0.15 }),
  variationPlugin(), // each glyph a little different
  strokeGradientPlugin(), // a rainbow along each stroke
  glowPlugin({ radius: 0.1, color: '#00ccff' }),
]

Each factory takes its options (or none, for the defaults). Where a plugin object can’t go (an HTML attribute, an Astro prop, a CLI flag), register the factories and name them instead:

import { glowPlugin, TegakiEngine, taperPlugin } from 'tegaki/core';

TegakiEngine.registerPlugin(glowPlugin, taperPlugin);

plugins: ['taper', ['glow', { radius: 0.1, color: '#00ccff' }]]

See Style plugins for every option, Naming plugins for the registry, and Plugins for writing your own. The Studio’s Plugins tab tries them out live, along with demo plugins whose source you can copy into your project.

These used to be the effects option, which is gone: see Migrating from effects.

For chat-like interfaces where text arrives incrementally, see Streaming Text.