@jimzer/remox
v0.1.10
Published
Zero-config Remotion renderer. Just write a composition.tsx and render.
Downloads
54
Maintainers
Readme
remox
Zero-config Remotion renderer. Write a single composition.tsx, run one command, get a video or image.
No package.json. No project scaffolding. No boilerplate.
Install
# requires bun: https://bun.sh
bun add -g @jimzer/remoxOr run directly without installing:
bunx @jimzer/remox render composition.tsx ./assetsUsage
# Render a video
remox render composition.tsx ./assets --output video.mp4
# Capture a still image
remox still composition.tsx ./assets --output poster.pngThat's it. remox will:
- Detect your imports and install dependencies automatically
- Scaffold a Remotion project behind the scenes
- Bundle, render, and output your video or image
Write a composition
// composition.tsx
import { AbsoluteFill, useCurrentFrame, spring, staticFile, Img } from "remotion";
export const config = {
width: 1920,
height: 1080,
fps: 30,
durationInFrames: 90,
};
export default function MyVideo() {
const frame = useCurrentFrame();
const scale = spring({ frame, fps: 30, config: { damping: 12 } });
return (
<AbsoluteFill style={{ background: "black", justifyContent: "center", alignItems: "center" }}>
<Img src={staticFile("bg.jpg")} style={{ position: "absolute", width: "100%", objectFit: "cover" }} />
<h1 style={{ color: "white", fontSize: 80, transform: `scale(${scale})` }}>Hello!</h1>
</AbsoluteFill>
);
}remox render composition.tsx ./assetsInline & stdin (for agents / automation)
remox supports receiving composition source directly — no file needed. This is ideal for LLM agents, scripts, and CI pipelines that generate video programmatically.
Stdin (recommended for agents)
Pipe TSX source directly into remox. Use - as the composition path:
# Heredoc
cat <<'EOF' | remox render - ./assets --output video.mp4
import { AbsoluteFill, useCurrentFrame } from "remotion";
export const config = { width: 1920, height: 1080, fps: 30, durationInFrames: 90 };
export default function() {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={{ background: "black", justifyContent: "center", alignItems: "center" }}>
<h1 style={{ color: "white", fontSize: 60 }}>Frame {frame}</h1>
</AbsoluteFill>
);
}
EOF
# Pipe from a file or command
echo "$TSX_SOURCE" | remox render - --output video.mp4
# From an agent that generates TSX
generate_composition | remox render - ./assets --output video.mp4Inline flag
Pass the TSX source as a string with --inline. Best for simple compositions:
remox render --inline '
import { AbsoluteFill } from "remotion";
export const config = { width: 1280, height: 720, fps: 30, durationInFrames: 60 };
export default () => <AbsoluteFill style={{ background: "linear-gradient(135deg, #667eea, #764ba2)" }} />;
'
# Also works with still
remox still --inline '
import { AbsoluteFill } from "remotion";
export const config = { width: 1200, height: 630 };
export default () => (
<AbsoluteFill style={{ background: "#1a1a2e", justifyContent: "center", alignItems: "center" }}>
<h1 style={{ color: "white", fontSize: 72 }}>OG Image</h1>
</AbsoluteFill>
);
' --output og.pngAgent integration example
Combine --inline for the template with --data for the content — or use a file-based template with different data each time:
import subprocess
import json
# Option 1: File template + data (best for reuse)
subprocess.run([
"remox", "render", "template.tsx", "./assets",
"--data", json.dumps({"title": "Generated by AI", "color": "#ff6600"}),
"--output", "output.mp4"
])
# Option 2: Inline template + data (fully dynamic)
tsx_source = """
import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
export const config = { width: 1920, height: 1080, fps: 30, durationInFrames: 150 };
export default function({ title, color }: { title: string; color: string }) {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
return (
<AbsoluteFill style={{ background: color, justifyContent: "center", alignItems: "center" }}>
<h1 style={{ color: "white", fontSize: 80, opacity }}>{title}</h1>
</AbsoluteFill>
);
}
"""
subprocess.run(
["remox", "render", "-", "--data", '{"title":"Hello","color":"#1a1a2e"}', "--output", "output.mp4"],
input=tsx_source.encode(),
)// Node.js / Bun
import { spawn } from "child_process";
const proc = spawn("remox", [
"render", "template.tsx", "./assets",
"--data", JSON.stringify({ title: "Hello", guest: "World" }),
"--output", "out.mp4"
]);Data / props (parametric rendering)
Pass dynamic data to your composition with --data. Your component receives it as props. This is the killer feature for agents and batch rendering — write one template, render it many times with different data.
Inline JSON
remox render template.tsx ./assets --data '{"title":"Episode 1","guest":"Alice"}' --output ep1.mp4
remox render template.tsx ./assets --data '{"title":"Episode 2","guest":"Bob"}' --output ep2.mp4JSON file
remox render template.tsx ./assets --data ./episode.json --output video.mp4Composition receives props
// template.tsx
import { AbsoluteFill, useCurrentFrame, spring, Img, staticFile } from "remotion";
export const config = { width: 1920, height: 1080, fps: 30, durationInFrames: 90 };
// Props come from --data
export default function VideoTemplate({ title, guest, theme }: {
title: string;
guest: string;
theme?: string;
}) {
const frame = useCurrentFrame();
const scale = spring({ frame, fps: 30, config: { damping: 12 } });
const bg = theme === "dark" ? "#0f0f0f" : "#ffffff";
const fg = theme === "dark" ? "#ffffff" : "#0f0f0f";
return (
<AbsoluteFill style={{ background: bg, justifyContent: "center", alignItems: "center" }}>
<div style={{ transform: `scale(${scale})`, textAlign: "center" }}>
<h1 style={{ color: fg, fontSize: 80 }}>{title}</h1>
<p style={{ color: fg, fontSize: 40, opacity: 0.7 }}>with {guest}</p>
</div>
</AbsoluteFill>
);
}Batch rendering example
#!/bin/bash
# Render a series of episodes
for i in 1 2 3 4 5; do
remox render template.tsx ./assets \
--data "{\"title\":\"Episode $i\",\"guest\":\"Guest $i\"}" \
--output "episodes/ep${i}.mp4"
doneAgent batch example
import subprocess
import json
episodes = [
{"title": "Intro to AI", "guest": "Alice", "theme": "dark"},
{"title": "Deep Learning", "guest": "Bob", "theme": "light"},
{"title": "LLM Agents", "guest": "Charlie", "theme": "dark"},
]
for i, ep in enumerate(episodes):
subprocess.run([
"remox", "render", "template.tsx", "./assets",
"--data", json.dumps(ep),
"--output", f"episodes/ep{i+1}.mp4"
])Render options
| Flag | Default | Description |
|------|---------|-------------|
| --output, -o | out/video.mp4 | Output file path |
| --width | 1920 | Video width |
| --height | 1080 | Video height |
| --fps | 30 | Frames per second |
| --frames | 150 | Duration in frames |
| --from | 0 | Start rendering from this frame |
| --to | last frame | Render up to this frame |
| --codec | h264 | Codec (h264, h265, vp8, vp9) |
| --data | | Props as inline JSON or path to JSON file |
Render a portion (useful for debugging)
remox render composition.tsx ./assets --from 0 --to 30Still options
| Flag | Default | Description |
|------|---------|-------------|
| --output, -o | out/still.png | Output file path |
| --width | 1920 | Image width |
| --height | 1080 | Image height |
| --frame | 0 | Which frame to capture |
| --format | png | Image format (png, jpeg, webp, pdf) |
| --quality | 80 | JPEG quality 0-100 |
| --scale | 1 | Scale factor (2 = double resolution) |
| --data | | Props as inline JSON or path to JSON file |
remox still composition.tsx ./assets --frame 45 --format jpeg --quality 90
remox still composition.tsx ./assets --scale 2 --output hi-res.pngConfig can also be exported from your composition:
export const config = { width: 1280, height: 720, fps: 60, durationInFrames: 300 };Assets
Put images, videos, fonts, etc. in a directory and pass it as the second argument. Reference them with staticFile():
<Img src={staticFile("logo.png")} />
<Video src={staticFile("clip.mp4")} />How it works
remox is a thin wrapper around Remotion's programmatic API. When you run remox render, it:
- Parses your
.tsxfile (or stdin/inline source) for import statements - Creates a temp project with the necessary Remotion boilerplate (
registerRoot,<Composition>, etc.) - Runs
bun addto install your dependencies - Calls
bundle()+renderMedia()/renderStill()from@remotion/bundlerand@remotion/renderer - Outputs your video or image and cleans up
Chrome Headless Shell is cached in ~/.remox/ after the first run so subsequent renders skip the download.
Requirements
- Bun (for dependency management and running)
- Chrome/Chromium (Remotion uses it for rendering — auto-downloaded on first run)
License
MIT
