Keyframe Animation
Animate scenes with CSS keyframes and render them as GIFs, WebP, APNG, or video frames.
Takumi supports two animation workflows:
- Use
renderAnimation()when you want a simple animatedwebp,apng, orgif. - Use
render()withtimeMswhen you want full control over frame rendering, structuredkeyframes, or external encoding tools like ffmpeg.
This is an example of a keyframe animation rendered with ffmpeg + shiki for syntax highlighting:
How to define animations
Structured keyframes objects
Use structured keyframes when you render a specific animation frame with render() and timeMs.
This is the API that accepts the keyframes option.
import { } from "@takumi-rs/core";
import { } from "@takumi-rs/helpers/jsx";
const = new ();
const { } = await (
< ="animate-[move_1s_ease-in-out_infinite_alternate]" />,
);
const = await .(, {
: 100,
: 100,
: "png",
: 500,
: {
: {
: {
: "translateX(0)",
},
"50%": {
: "translateX(60px)",
},
: {
: "translateX(120px)",
},
},
},
});CSS Stylesheets
Use stylesheet @keyframes when you want the animation to travel with the JSX tree and then render it with either renderAnimation() or render() + stylesheets.
import { } from "@takumi-rs/core";
import { } from "@takumi-rs/helpers/jsx";
const = new ();
const { , } = await (
< ="w-full h-full items-center justify-center">
<>{`
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(60px);
}
}
`}</>
< ="w-10 h-10 bg-red-500 animate-[move_1s_ease-in-out_infinite_alternate]" />
</>,
);
const = await .({
: 100,
: 100,
: 30,
: "webp",
,
: [
{
: 1000,
,
},
],
});Tailwind animation utilities
Takumi supports these Tailwind animation forms:
- Presets:
animate-none,animate-spin,animate-ping,animate-pulse,animate-bounce - Arbitrary shorthand values:
animate-[move_1s_ease-in-out_infinite_alternate]
For arbitrary values, _ is converted to a space, so:
animate-[move_1s_ease-in-out_infinite_alternate]becomes:
animation: move 1s ease-in-out infinite alternate;Takumi does not currently support Tailwind's animate-(--custom-property) form because CSS custom property resolution for animation is not implemented.
Ways to render
The next two examples share this file.
import type { } from "@takumi-rs/core";
export const : = {
: {
: {
: "translateX(0)",
},
: {
: "translateX(60px)",
},
},
};
export function () {
return (
< ="w-full h-full items-center justify-center">
< ="w-10 h-10 bg-red-500 animate-[move_1s_ease-in-out_infinite_alternate]" />
</>
);
}render()
Use render() when you want a single frame at a specific animation time.
import { } from "@takumi-rs/core";
import { } from "@takumi-rs/helpers/jsx";
import { , } from "./scene";
const = new ();
const { } = await (< />);
const = await .(, {
: 100,
: 100,
: "png",
,
: 500,
});renderAnimation()
This is the minimal API for animated image output. In most cases, you should keep a single scene and animate it with stylesheet keyframes or Tailwind's built-in animation presets. The scenes field is still an array because you may want to compose multiple scenes into a single animated image, but Takumi does not generate transitions between them.
import { } from "@takumi-rs/core";
import { } from "@takumi-rs/helpers/jsx";
const = new ();
const { , } = await (
< ="w-full h-full items-center justify-center">
<>{`
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(60px);
}
}
`}</>
< ="w-10 h-10 bg-red-500 animate-[move_1s_ease-in-out_infinite_alternate]" />
</>,
);
const = await .({
: 100,
: 100,
: 30,
: "webp",
,
: [
{
: 1000,
,
},
],
});render() + ffmpeg
The ffmpeg-keyframe-animation example renders raw frames with render() and streams them into ffmpeg. This is the better route when you want video output or tighter control over the pipeline.
The core idea is:
- Build the scene once.
- Render each frame at a specific
timeMs. - Pipe the raw RGBA frames into ffmpeg.
import { } from "@takumi-rs/core";
import { } from "@takumi-rs/helpers/jsx";
import { } from "bun";
import { , } from "./scene";
const = new ();
const = 30;
const = 4;
const = 1200;
const = 630;
const = * ;
const { } = await (< />);
const = (
[
"ffmpeg",
"-y",
"-f",
"rawvideo",
"-pixel_format",
"rgba",
"-video_size",
`${}x${}`,
"-framerate",
`${}`,
"-i",
"pipe:0",
"output.mp4",
],
{ : "pipe", : "ignore", : "ignore" },
);
for (let = 0; < ; ++) {
const = ( / ) * 1000;
const = await .(, {
,
,
: "raw",
,
,
});
..();
}
..();
await .;Last updated on