{"id":28708312,"url":"https://github.com/kane50613/takumi","last_synced_at":"2026-06-12T06:05:38.934Z","repository":{"id":298705848,"uuid":"994110247","full_name":"kane50613/takumi","owner":"kane50613","description":"Render JSX, HTML, and CSS to images without a headless browser. OG cards, animated GIFs, and video frames from Node.js, edge runtimes, browsers, or Rust. Drop-in next/og replacement.","archived":false,"fork":false,"pushed_at":"2026-06-10T00:38:42.000Z","size":256940,"stargazers_count":1859,"open_issues_count":10,"forks_count":37,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-10T02:19:53.080Z","etag":null,"topics":["cloudflare-workers","css","gif","html-to-image","image-generation","jsx","nodejs","og-image","opengraph","react","rust","satori","tailwindcss","wasm"],"latest_commit_sha":null,"homepage":"https://takumi.kane.tw","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kane50613.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"buy_me_a_coffee":"kanewang"}},"created_at":"2025-06-01T08:19:38.000Z","updated_at":"2026-06-10T00:20:18.000Z","dependencies_parsed_at":"2026-02-28T09:11:25.909Z","dependency_job_id":null,"html_url":"https://github.com/kane50613/takumi","commit_stats":null,"previous_names":["kane50613/takumi"],"tags_count":1272,"template":false,"template_full_name":null,"purl":"pkg:github/kane50613/takumi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kane50613%2Ftakumi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kane50613%2Ftakumi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kane50613%2Ftakumi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kane50613%2Ftakumi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kane50613","download_url":"https://codeload.github.com/kane50613/takumi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kane50613%2Ftakumi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34229624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare-workers","css","gif","html-to-image","image-generation","jsx","nodejs","og-image","opengraph","react","rust","satori","tailwindcss","wasm"],"created_at":"2025-06-14T18:10:34.258Z","updated_at":"2026-06-12T06:05:38.918Z","avatar_url":"https://github.com/kane50613.png","language":"Rust","funding_links":["https://buymeacoffee.com/kanewang"],"categories":["Rust","Miscellaneous"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/images/takumi.svg\" alt=\"Takumi\" width=\"64\" /\u003e\n\n# Takumi\n\n**A Rust rendering engine that turns JSX, HTML, and node trees into images. No headless browser required.**\n\nRender OpenGraph cards, animated GIFs, and video frames from Node.js, Cloudflare Workers, browsers, or any Rust application.\nDrop-in compatible with `next/og`.\n\n[![npm version](https://img.shields.io/npm/v/takumi-js?label=takumi-js)](https://www.npmjs.com/package/takumi-js)\n[![crates.io](https://img.shields.io/crates/v/takumi?label=takumi)](https://crates.io/crates/takumi)\n[![npm downloads](https://img.shields.io/npm/dm/%40takumi-rs%2Fcore?label=downloads)](https://www.npmjs.com/package/@takumi-rs/core)\n[![license](https://img.shields.io/badge/license-MIT%20%2F%20Apache--2.0-blue)](#license)\n\n[Documentation](https://takumi.kane.tw/docs/) · [Playground](https://takumi.kane.tw/playground) · [Showcase](https://takumi.kane.tw/showcase)\n\n\u003c/div\u003e\n\n## Why Takumi\n\nTakumi is a rendering pipeline built in Rust for one job: turning markup and CSS into pixels. It parses CSS, lays out the tree, shapes text, composites layers, and encodes the output inside a single binary. A headless-Chromium setup spends around 300 MB of RAM and a browser cold start on the same OG card; Takumi spends a function call.\n\nOne engine covers every deployment target. Node.js servers load the native binding, Cloudflare Workers and browsers load the WASM build, and Rust applications embed the `takumi` crate. Prebuilt binaries ship for macOS, Linux (glibc and musl), and Windows, on both x64 and ARM64.\n\nThe CSS support reaches past the usual OG-image subset: CSS Grid, `::before` and `::after`, `:is()` and `:where()` selectors, masks and `clip-path`, `backdrop-filter`, `background-clip: text`, conic gradients, RTL text, and Tailwind v4 utilities including arbitrary values.\n\n## Quick Start\n\n```bash\nbun i takumi-js\n```\n\n### Static image\n\n```tsx\nimport { render } from \"takumi-js\";\nimport { writeFile } from \"node:fs/promises\";\n\nconst image = await render(\n  \u003cdiv tw=\"w-full h-full flex items-center justify-center bg-gradient-to-b from-blue-100 to-red-50\"\u003e\n    \u003ch1 tw=\"text-6xl font-bold\"\u003eHello from Takumi\u003c/h1\u003e\n  \u003c/div\u003e,\n  { width: 1200, height: 630 },\n);\n\nawait writeFile(\"./output.png\", image);\n```\n\n### API route (`next/og`-compatible)\n\n```tsx\nimport { ImageResponse } from \"takumi-js/response\";\n\nexport function GET() {\n  return new ImageResponse(\n    \u003cdiv tw=\"w-full h-full flex items-center justify-center bg-gradient-to-b from-blue-100 to-red-50\"\u003e\n      \u003ch1 tw=\"text-6xl font-bold\"\u003eHello from Takumi\u003c/h1\u003e\n    \u003c/div\u003e,\n    { width: 1200, height: 630 },\n  );\n}\n```\n\n### Animated WebP\n\n```tsx\nimport { Renderer } from \"takumi-js/node\";\nimport { fromJsx } from \"takumi-js/helpers/jsx\";\nimport { writeFile } from \"node:fs/promises\";\n\nconst renderer = new Renderer();\n\nconst { node, stylesheets } = await fromJsx(\n  \u003cdiv tw=\"w-full h-full flex items-center justify-center\"\u003e\n    \u003cdiv tw=\"w-32 h-32 bg-blue-500 animate-spin rounded-lg\" /\u003e\n  \u003c/div\u003e,\n);\n\nconst animation = await renderer.renderAnimation({\n  width: 400,\n  height: 400,\n  fps: 30,\n  format: \"webp\",\n  stylesheets,\n  scenes: [{ durationMs: 1000, node }],\n});\n\nawait writeFile(\"./output.webp\", animation);\n```\n\n### Rust\n\n```bash\ncargo add takumi\n```\n\nStart from the [Rust example](./example/rust).\n\n## Comparison\n\n| Feature                            | `next/og` (Satori) |                            Takumi                             |\n| :--------------------------------- | :----------------: | :-----------------------------------------------------------: |\n| **Runtime**                        |    Node / Edge     |        Node, Edge, CF Workers, Browser, **Rust crate**        |\n| **Template input**                 |    JSX / React     |     JSX, HTML strings, JSON node trees from any language      |\n| **Layout**                         |      Flexbox       |          Flexbox, **CSS Grid**, block, inline, float          |\n| **Selectors**                      |      Limited       | Complex selectors, `:is()`, `:where()`, `::before`, `::after` |\n| **`backdrop-filter`, blend modes** |         ✗          |                              ✅                               |\n| **Animated output**                |         ✗          |             **WebP / APNG / GIF / video frames**              |\n| **Headless browser**               |         ✗          |                               ✗                               |\n| **`ImageResponse` API**            |     ✅ Native      |                       ✅ **Compatible**                       |\n\nCompare rendering output across providers at [image-bench.kane.tw](https://image-bench.kane.tw).\n\n## Who's Using Takumi\n\n- [Dcard](https://dcard.tw) renders post share images\n- [Fumadocs](https://fumadocs.dev) generates its docs OG images\n- [Nuxt OG Image](https://nuxtseo.com/docs/og-image/renderers/takumi) ships Takumi as a built-in renderer\n- [shiki-image](https://github.com/pi0/shiki-image) turns syntax-highlighted code into images\n\nMore projects in the [showcase](https://takumi.kane.tw/showcase). Takumi is part of the [Vercel OSS Program](https://vercel.com/oss).\n\n## Core Architecture\n\nTakumi converts any template into a **node tree** with three node kinds: `container`, `image`, and `text`. That tree runs through:\n\n1. **Layout** via [taffy](https://github.com/DioxusLabs/taffy): Flexbox, Grid, block, float, `calc()`, absolute positioning, z-index\n2. **Text shaping** via [parley](https://github.com/linebender/parley) and [skrifa](https://github.com/googlefonts/fontations/tree/main/skrifa): WOFF/WOFF2 fonts, emoji, RTL, multi-span inline blocks\n3. **Compositing**: stacking contexts, blend modes, filters, transforms, SVG via [resvg](https://github.com/linebender/resvg)\n4. **Output**: PNG, JPEG, WebP, ICO for statics; GIF, APNG, WebP for animations; raw RGBA frames for video pipelines\n\nThe input contract is a node tree, so any template system that serializes to HTML or JSON can feed it: React, Svelte, Vue, plain strings, or your own serializer in any language.\n\nA **time axis** threads through the pipeline: the renderer takes a timestamp, so a PNG is the tree at `t=0` and a GIF is the same tree sampled across `t`. CSS `@keyframes`, the `animation` shorthand, and Tailwind animation utilities (`animate-spin`, `animate-bounce`, arbitrary values) all resolve at render time.\n\n```mermaid\nflowchart LR\n    A[Templates] --\u003e N[Node Tree] --\u003e P[Rendering Pipeline] --\u003e F[(Raw Pixels)]\n    C[Stylesheets] --\u003e P\n    R[Resources] --\u003e P\n    D(Time Axis) -.-\u003e P\n\n    F --\u003e G[PNG / JPEG / WebP / ICO]\n    F --\u003e H[GIF / APNG]\n    F --\u003e I[Video frames]\n```\n\n## Showcase\n\n|                                 Takumi OG image [(source)](./example/twitter-images/components/og-image.tsx)                                 |                Package OG card [(source)](./example/twitter-images/components/package-og-image.tsx)                 |\n| :------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------: |\n|                                       ![Takumi OG Image](./example/twitter-images/output/og-image.png)                                       |                      ![Package OG Image](./example/twitter-images/output/package-og-image.png)                      |\n|                        **Prisma-style API card** [(source)](./example/twitter-images/components/prisma-og-image.tsx)                         |              **X-style social post** [(source)](./example/twitter-images/components/x-post-image.tsx)               |\n|                                   ![Prisma OG Image](./example/twitter-images/output/prisma-og-image.png)                                    |                       ![X-style Post Image](./example/twitter-images/output/x-post-image.png)                       |\n|                             **Keyframe Animation** [(source)](./example/ffmpeg-keyframe-animation/src/index.tsx)                             |                                **[shiki-image](https://github.com/pi0/shiki-image)**                                |\n| [![Keyframe Animation](./example/ffmpeg-keyframe-animation/output/thumbnail.webp)](./example/ffmpeg-keyframe-animation/output/animation.mp4) | ![Shiki Image Example](https://raw.githubusercontent.com/pi0/shiki-image/refs/heads/main/test/.snapshot/image.webp) |\n\n**More examples:** [Next.js](./example/nextjs), [Cloudflare Workers](./example/cloudflare-workers), [TanStack Start](./example/tanstack-start), [Svelte](./example/svelte), [Rust](./example/rust), [ffmpeg keyframe animation](./example/ffmpeg-keyframe-animation)\n\n- [(Unofficial) Takumi Playground](https://takumi-playground.kapadiya.net/)\n\n## Contributing\n\nRead [CONTRIBUTING.md](./CONTRIBUTING.md). Covers local setup, test commands, fixture workflow, and changeset process.\n\nWe welcome bug reports, feature requests, doc improvements, and new example integrations.\n\n## License\n\nMIT or Apache-2.0\n\n\u003cbr/\u003e\n\u003ca href=\"https://vercel.com/oss\"\u003e\n  \u003cimg alt=\"Vercel OSS Program\" src=\"https://vercel.com/oss/program-badge.svg\" /\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkane50613%2Ftakumi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkane50613%2Ftakumi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkane50613%2Ftakumi/lists"}