TakumiTakumi

Animations

CSS keyframes, timing functions, and multi-scene output to WebP / GIF / APNG.

Moved from /docs/keyframe-animation.

For animated image output, you have two APIs:

  • render() with timeMs for a single frame at a specific moment.
  • renderAnimation() for a complete WebP / GIF / APNG.

For video output (MP4, high frame rate, ffmpeg pipelines), see Video Frames.

CSS @keyframes

Define a @keyframes rule inline or in a stylesheet; attach it with the animation shorthand.

import {  } from "takumi-js/node";
import {  } from "takumi-js/helpers/jsx";

const  = new ();

const { ,  } = await (
  < ="w-full h-full items-center justify-center">
    <>{`
      @keyframes slide {
        from { transform: translateX(0); }
        50%  { transform: translateX(120px); }
        to   { transform: translateX(0); }
      }
    `}</>
    < ="w-16 h-16 bg-blue-500 animate-[slide_2s_ease-in-out_infinite]" />
  </>,
);

await .({
  : 400,
  : 200,
  : 30,
  : "webp",
  ,
  : [{ : 2000,  }],
});

The structured form is equivalent and lives in the JS layer:

import {  } from "takumi-js";

await (< ="animate-[slide_2s_ease-in-out_infinite]" />, {
  : 400,
  : 200,
  : "png",
  : 1000,
  : {
    : {
      : { : "translateX(0)" },
      "50%": { : "translateX(120px)" },
      : { : "translateX(0)" },
    },
  },
});

Stylesheet keyframes travel with the JSX tree; structured keyframes stay in user space. Pick whichever matches how your project is organized.

Timing functions

Standard CSS timing functions all work, including custom cubic-beziers and steps().

<div style={{ animation: "slide 2s ease-in-out infinite" }} />
<div style={{ animation: "slide 2s linear infinite" }} />
<div style={{ animation: "slide 2s cubic-bezier(0.32, 0, 0.67, 0) infinite" }} />
<div style={{ animation: "tick 1s steps(4, end) infinite" }} />

steps() is the right pick for sprite-like animations where you want discrete frames; the cubic-bezier form covers anything the four named easings can't express.

Multi-scene animations

renderAnimation takes an array of scenes. Each scene is a node + a duration. Scenes play back to back; there is no transition between them — if you want a crossfade, render it as an animation inside one scene.

import {  } from "takumi-js/node";
import {  } from "takumi-js/helpers/jsx";

const  = new ();
const  = await (<Intro />);
const  = await (<Main />);
const  = await (<Outro />);

await .({
  : 1200,
  : 630,
  : 30,
  : "webp",
  : [...., ...., ....],
  : [
    { : 1000, : . },
    { : 3000, : . },
    { : 1000, : . },
  ],
});

In most cases you'll keep a single scene and let CSS handle the motion. Reach for multiple scenes when the layout itself changes — different nodes, not just different transforms.

Tailwind animation utilities

These forms compile straight through to animation shorthand:

  • Presets: animate-none, animate-spin, animate-ping, animate-pulse, animate-bounce.
  • Arbitrary values: animate-[slide_2s_ease-in-out_infinite_alternate] — underscores become spaces.
<div tw="w-12 h-12 bg-blue-500 animate-spin rounded" />
<div tw="w-12 h-12 bg-red-500 animate-[slide_2s_ease-in-out_infinite_alternate]" />

animate-(--custom-property) is not supported — CSS custom property resolution for animation shorthand is not implemented.

Format choice

FormatTransparencyCompressionAnimation lengthUse for
Animated WebPYesBestShort / mediumDefault. Modern browsers.
APNGYesGoodShort / mediumCompatibility with older clients.
GIF1-bit onlyWorstShortEmail, embeds that reject WebP.

Default to animated WebP unless you have a known consumer that doesn't accept it. For anything more than a few seconds at 30 fps, switch to video — see Video Frames.

Edit on GitHub

Last updated on

On this page