Skip to content

Vanilla JS

You can use TegakiEngine directly with plain JavaScript or TypeScript, without a framework.

npm install tegaki
<div id="tegaki" style="font-size: 48px"></div>

<script type="module">
  import { TegakiEngine } from 'tegaki/core';
  import bundle from 'tegaki/fonts/caveat';

  const container = document.getElementById('tegaki');

  const engine = new TegakiEngine(container, {
    text: 'Hello World',
    font: bundle,
    time: { mode: 'uncontrolled', speed: 1, loop: true },
  });
</script>

The engine creates its DOM elements inside the container.

Call update() to change text, font, time, or plugins at any time:

import { glowPlugin } from 'tegaki/core';

engine.update({ text: 'New text' });

engine.update({
  plugins: [glowPlugin({ radius: 0.1, color: '#00ccff' })],
});

Pass a number to time and update it on each frame:

const engine = new TegakiEngine(container, {
  text: 'Controlled',
  font: bundle,
  time: 0,
});

let t = 0;
function animate() {
  t += 0.016;
  engine.update({ time: t });
  requestAnimationFrame(animate);
}
animate();
Drag the slider to scrub through the animation

Glows, gradients, tapered ends and the rest are plugins. Import the factories from tegaki/core and pass the plugins in a list:

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

const engine = new TegakiEngine(container, {
  text: 'Glowing rainbow',
  font: bundle,
  time: { mode: 'uncontrolled', speed: 1, loop: true },
  plugins: [strokeGradientPlugin(), glowPlugin({ radius: 0.1, color: '#00ccff' })],
});

Register a factory and plugins can name it, with or without options. That’s handy when the list comes from data (a JSON config, a data- attribute):

TegakiEngine.registerPlugin(glowPlugin, strokeGradientPlugin);

engine.update({ plugins: ['strokeGradient', ['glow', { radius: 0.1, color: '#00ccff' }]] });

See Style plugins and Naming plugins. The pressure option (0 to 1, default 1) sets how much the ink’s width follows the pen’s pressure.

Call destroy() when you’re done to remove event listeners and stop the animation loop:

engine.destroy();

Register bundles globally so you can reference them by name:

import { TegakiEngine } from 'tegaki/core';
import caveat from 'tegaki/fonts/caveat';

TegakiEngine.registerBundle(caveat);

// Later, anywhere in your app:
const engine = new TegakiEngine(container, {
  text: 'Hello',
  font: 'Caveat', // resolved from the registry
});

Use TegakiEngine.renderElements() to produce the element tree on the server, then adopt it on the client:

import { TegakiEngine } from 'tegaki/core';

// Server: render to HTML string
const html = TegakiEngine.renderElements(
  { text: 'Hello', font: bundle },
  (tag, props, ...children) => {
    // your HTML string builder
  },
);

// Client: adopt pre-rendered DOM
const engine = new TegakiEngine(container, { adopt: true, text: 'Hello', font: bundle });

This is what all framework adapters use internally.