Generate videos
from familiar web code,
without browsers.

Render static images and frames server-side with JSX and CSS, no browsers involved. Compose videos in declarative JSON. Tie it all together over HTTP.

MIT-licensed and runs on nothing but Node and FFmpeg. Nice and simple, made for scale, and no vendor lock-in.

$ npm create @effing my-effing-app

Keep it simple, FFS.

You build a video by combining simple pieces: images for static visuals, annies for animations, and an effie that says how they all fit together. FFS, our FFmpeg-based rendering service, is what turns effies into MP4s. That's all there is to it, really — but it's surprisingly powerful.

Image
A single PNG or JPEG
Static visuals built from typed props. Render them with JSX, or however you like. Useful on their own, or inside a video as cover frames, overlays, or backgrounds.
Annie
A streamable animation
A streamable TAR archive of PNG or JPEG frames. Stream frames out as you generate them. Playable in the browser, composable into videos.
Effie
A declarative composition
Typed JSON describing how images, annies, audio, transitions, and effects combine into a final video. Write it anywhere, hand it to FFS, out comes an MP4.
Learn more in the fundamentals guide →

Browserless JSX, holy flex.

Effing Canvas is a server-side canvas that turns JSX and CSS into pixels — no DOM involved. It lets you write the same flexbox model and CSS properties you use on the web. Yoga handles layout, Skia handles rendering. This makes rendering images and annie frames easy.

title.tsx
const width = 1080, height = 1920
const canvas = createCanvas(width, height)
const ctx = canvas.getContext("2d")

await renderReactElement(ctx,
  <div style={{
    width, height,
    display: "flex", alignItems: "center", justifyContent: "center",
    backgroundColor: "#151716", fontSize: 80, color: "#4CAE4C",
  }}>My Video</div>,
)

const png = await canvas.encode("png")

Flexbox layout

Powered by Yoga. The same model you use in CSS and React Native.

CSS properties

Fonts, gradients, borders, shadows, transforms, opacity — styled the way you expect.

Emoji support

Automatic emoji rendering from CDN sources — Twemoji, Noto, Fluent, and more.

Lottie as well

Not just JSX. You can also render After Effects animations frame by frame, via Skottie, on the same canvas.

It's all fn modules.

Write your images, annies, and effies as @effing/fn modules — they pair a function with a props schema and preview defaults. Point to them in effing.config.ts and run effing dev to boot a preview app, or effing build to bundle a production server. That's it!

app/images/hello.fn.tsx
import { z } from "zod"
import type { RunnerArgs, ImageRunnerReturn } from "@effing/fn"
import { createCanvas, renderReactElement } from "@effing/canvas"

export const propsSchema = z.object({
  title: z.string(),
  accent: z.string().default("#4CAE4C"),
})

type HelloProps = z.infer<typeof propsSchema>

export const previewProps: HelloProps = { title: "Hello, world" }

export async function runner({
  props: { title, accent },
  bounds: { width, height },
}: RunnerArgs<HelloProps>): ImageRunnerReturn {
  const canvas = createCanvas(width, height)
  await renderReactElement(canvas.getContext("2d"),
    <div style={{
      width, height,
      display: "flex", alignItems: "center", justifyContent: "center",
      backgroundColor: "#151716", fontSize: 96, color: accent,
    }}>{title}</div>,
  )
  return canvas.encode("png")
}

Live preview UI

An overview page lists every image, annie, and effie. Per-module pages have a resolution picker and auto-reload on file save.

Node production server

Build a self-contained dist/server.js that exposes every fn as a signed URL. Run it with just node.

Inspectable by anyone

HTML preview pages let humans click through every fn. Raw .bytes, .tar, and .json endpoints alongside let agents fetch artifacts directly.

Agents can RTFM

Run effing manual for a comprehensive dev guide. Put that in your AGENTS.md so your agents know exactly how it's done.

Learn more in the tutorial →

Modular AF.

Twelve focused TypeScript packages. Use the whole pipeline, or pull in just the pieces you need.

See all packages on GitHub →

Start making videos.

Scaffold a project, preview in the browser, render to MP4.

$ npm create @effing my-effing-app
Effing Cloud
Ship your effing code. Skip the infra.

Effing Cloud hosts your fn modules and the FFS rendering for you — same fns, but nothing to babysit.

Try Effing Cloud →