Same renderer. Your framework.

Working integrations.

These are real framework mounts, not visual mockups. Each example uses the packaged adapter and the same compiled Amiri asset. Font license.

Core playback needs no animation library. Use a recipe when you already have a timeline to coordinate.

Frameworks

Play, scrub, replace the asset, or unmount and mount the component.

Animation recipes

One normalized playhead.

GSAP, Motion and Anime.js animate progress from 0 to 1. Each recipe is a separate, optional entry point.

Choose an animation engine.
// Native playback needs no extra engine:
controller.seek(0);
controller.play({ duration: 3000 }); // milliseconds

// Or import a recipe only when its engine is installed:
import { gsapInk } from 'arabic-ink/recipes/gsap';
const { animation, dispose } = gsapInk(controller, { duration: 3 });
// Recipe durations use seconds. Dispose before unmounting.

Compile first

The smallest setup.

The source is local and publish-ready; the npm name is not published or reserved yet. In a local checkout:

bun install
bun run build
bun dist/compiler/cli.js --font ./font.ttf \
  --text "نكتب بالعربية" --out ./public/hello

// In the browser:
import { loadInk, mountInk } from 'arabic-ink';
const asset = await loadInk('/hello.json');
const view = mountInk(document.querySelector('#hello'), asset);
// On teardown:
view.destroy();

For large numbers of concurrent instances, share one createInkScene() instead of mounting hundreds of separate canvas components. Always retain readable fallback text.