npm package: @jimzer/remox
Description: Zero-config Remotion renderer. Just write a composition.tsx and render.
Latest version: 0.1.10

# 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

```bash
# requires bun: https://bun.sh
bun add -g @jimzer/remox
```

Or run directly without installing:

```bash
bunx @jimzer/remox render composition.tsx ./assets
```

## Usage

```bash
# Render a video
remox render composition.tsx ./assets --output video.mp4

# Capture a still image
remox still composition.tsx ./assets --output poster.png
```

That's it. remox will:

1. Detect your imports and install dependencies automatically
2. Scaffold a Remotion project behind the scenes
3. Bundle, render, and output your video or image

## Write a composition

```tsx
// 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>
  );
}
```

```bash
remox render composition.tsx ./assets
```

## Inline & 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:

```bash
# 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.mp4
```

### Inline flag

Pass the TSX source as a string with `--inline`. Best for simple compositions:

```bash
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.png
```

### Agent integration example

Combine `--inline` for the template with `--data` for the content — or use a file-based template with different data each time:

```python
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(),
)
```

```javascript
// 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

```bash
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.mp4
```

### JSON file

```bash
remox render template.tsx ./assets --data ./episode.json --output video.mp4
```

### Composition receives props

```tsx
// 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

```bash
#!/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"
done
```

### Agent batch example

```python
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)

```bash
remox render composition.tsx ./assets --from 0 --to 30
```

## Still 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 |

```bash
remox still composition.tsx ./assets --frame 45 --format jpeg --quality 90
remox still composition.tsx ./assets --scale 2 --output hi-res.png
```

Config can also be exported from your composition:

```tsx
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()`:

```tsx
<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:

1. Parses your `.tsx` file (or stdin/inline source) for import statements
2. Creates a temp project with the necessary Remotion boilerplate (`registerRoot`, `<Composition>`, etc.)
3. Runs `bun add` to install your dependencies
4. Calls `bundle()` + `renderMedia()` / `renderStill()` from `@remotion/bundler` and `@remotion/renderer`
5. 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](https://bun.sh) (for dependency management and running)
- Chrome/Chromium (Remotion uses it for rendering — auto-downloaded on first run)

## License

MIT