Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
532 changes: 268 additions & 264 deletions bun.lock

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions docs/animation/interpolate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# interpolate()

The `interpolate()` function maps an input value (usually the current frame) from one range to another. It's the most common way to create animations in MotionForge.

## Basic Usage

```tsx
import { useCurrentFrame, interpolate } from 'motionforge';

const MyComponent = () => {
const frame = useCurrentFrame();

// Animate opacity from 0 to 1 over frames 0 to 30
const opacity = interpolate(frame, [0, 30], [0, 1]);

return <div style={{ opacity }}>Hello World</div>;
};
```

## Advanced Options

```tsx
const value = interpolate(frame, [0, 30, 60], [0, 100, 50], {
extrapolateLeft: 'clamp',
extrapolateRight: 'wrap',
easing: Easing.easeOutCubic,
posterize: 10
});
```

### Parameters

| Parameter | Type | Description |
| :--- | :--- | :--- |
| `input` | `number` | The value to interpolate (usually `frame`). |
| `inputRange` | `number[]` | Array of input values. Must be increasing. |
| `outputRange` | `number[]` | Array of output values. Must be same length as `inputRange`. |
| `options` | `Object` | Optional configuration. |

### Options

| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `extrapolateLeft` | `string` | `'clamp'` | How to handle values below `inputRange[0]`. Options: `'clamp'`, `'extend'`, `'identity'`, `'wrap'`. |
| `extrapolateRight` | `string` | `'clamp'` | How to handle values above the last `inputRange` element. Options: `'clamp'`, `'extend'`, `'identity'`, `'wrap'`. |
| `easing` | `Easing \| Easing[]` | `undefined` | Easing function(s) to apply. Can be an array for per-segment easing. |
| `posterize` | `number` | `undefined` | If set, the input will be quantized to steps of this size (stepped animation). |

---

## Extrapolation Modes

- **`clamp`**: (Default) Clamps the result to the output range.
- **`extend`**: Continues the linear interpolation beyond the range.
- **`identity`**: Returns the input value if it's outside the range.
- **`wrap`**: (New) Cycles the value back into the range (looping animation).
68 changes: 68 additions & 0 deletions docs/animation/spring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# spring()

The `spring()` function creates physics-based animations that feel natural and organic. Unlike linear interpolation, spring animations have momentum and can overshoot their target.

## Basic Usage

```tsx
import { useCurrentFrame, useVideoConfig, spring } from 'motionforge';

const MyComponent = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();

const scale = spring({
frame,
fps,
from: 0,
to: 1,
config: {
damping: 12,
stiffness: 100
}
});

return (
<div style={{ transform: `scale(${scale})` }}>
Animated Box
</div>
);
};
```

## Parameters

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `frame` | `number` | **Required** | The current frame. |
| `fps` | `number` | **Required** | Frames per second of the composition. |
| `from` | `number` | `0` | Starting value. |
| `to` | `number` | `1` | Target value. |
| `delay` | `number` | `0` | Frames to wait before starting the animation. |
| `reverse` | `boolean` | `false` | If true, plays the animation from `to` to `from`. |
| `durationInFrames`| `number` | `fps * 2` | Approximate duration until the spring settles. |
| `config` | `Object` | See below | Spring physics configuration. |

### Config Options

| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `stiffness` | `number` | `100` | The spring tension. Higher = faster. |
| `damping` | `number` | `10` | Friction. Lower = more "bouncy" oscillation. |
| `mass` | `number` | `1` | Weight of the object. |
| `overshootClamping`| `boolean`| `false` | If true, prevents the value from going past `to`. |

---

## Reverse Spring (Exit Animations)

Use the `reverse` prop to easily create exit animations using the same spring physics.

```tsx
const opacity = spring({
frame,
fps,
delay: 200, // Wait until frame 200
reverse: true, // Fade out
});
```
71 changes: 71 additions & 0 deletions docs/api-reference/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# API Reference: Components

## `<AbsoluteFill />`

A container that fills the entire canvas area. It defaults to `position: absolute` and covers the whole parent.

### Props

| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `className` | `string` | `undefined` | Tailwind CSS classes. |
| `style` | `CSSProperties`| `{}` | Inline styles. |
| `children` | `ReactNode` | `undefined` | Children to render. |

> **Tailwind Compatibility**: MotionForge automatically detects if you use layout-related Tailwind classes (like `w-1/2`, `inset-0`, `grid`) and omits conflicting default inline styles.

---

## `<Sequence />`

Used to manage timing and layering. It only renders its children during the specified frame range.

### Props

| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `from` | `number` | **Required** | Start frame (inclusive). |
| `durationInFrames` | `number` | `Infinity` | How many frames to render for. |
| `premountFor` | `number` | `0` | Render children `N` frames before `from` (invisible) for preloading. |
| `layout` | `string` | `'absolute-fill'` | Layout mode: `'absolute-fill'` or `'none'`. |
| `name` | `string` | `undefined` | Debug label. |

---

## `<Series />`

Plays a list of sequences one after another.

### Usage

```tsx
<Series>
<Series.Sequence durationInFrames={30}>
<FirstScene />
</Series.Sequence>
<Series.Sequence durationInFrames={60} offset={-5}>
<SecondScene /> {/* Overlaps first scene by 5 frames */}
</Series.Sequence>
</Series>
```

### `<Series.Sequence />` Props

| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `durationInFrames` | `number` | **Required** | Duration of this segment. |
| `offset` | `number` | `0` | Gap (positive) or overlap (negative) with previous segment. |

---

## `<Freeze />`

Freezes a single frame for a specific duration.

### Props

| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `frame` | `number` | **Required** | The frame index to freeze at. |
| `durationInFrames` | `number` | `undefined` | If set, freezes for this many frames then resumes. |
| `active` | `boolean \| Fn` | `true` | Conditional freeze toggle. |
70 changes: 70 additions & 0 deletions docs/cli/cli-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# CLI Commands

The `@motionforge/cli` package provides tools to render videos and stills from the command line.

## `render`

Render a composition to a video file.

```bash
motionforge render <entry> <compositionId> [options]
```

### Options

| Option | Default | Description |
| :--- | :--- | :--- |
| `-o, --output` | `output.mp4` | Path to save the video. |
| `--codec` | `h264` | Video codec: `h264`, `h265`, `vp8`, `vp9`, `prores`, `gif`. |
| `--fps` | `30` | Frame rate. |
| `--width` | `1920` | Video width. |
| `--height` | `1080` | Video height. |
| `--duration` | `300` | Duration in frames. |
| `--props` | `undefined` | JSON string of input props to inject. |
| `--concurrency` | `4` | Number of concurrent browser pages for rendering. |
| `--frame-range` | `undefined` | Range to render, e.g., `0-29`. |
| `--crf` | `18` | Constant Rate Factor (quality). |

---

## `still`

Render a single frame to an image file.

```bash
motionforge still <entry> <compositionId> [options]
```

### Options

| Option | Default | Description |
| :--- | :--- | :--- |
| `-o, --output` | `output.png` | Path to save the image. |
| `--frame` | `0` | The frame number to render. |
| `--props` | `undefined` | JSON string of input props. |

---

## `compositions`

List all compositions registered in the entry file.

```bash
motionforge compositions <entry>
```

---

## `studio`

Start the MotionForge Studio development environment.

```bash
motionforge studio <entry> [options]
```

### Options

| Option | Default | Description |
| :--- | :--- | :--- |
| `-p, --port` | `3123` | Port to run the studio on. |
63 changes: 63 additions & 0 deletions docs/core-concepts/compositions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Compositions

Compositions are the building blocks of MotionForge videos. A composition defines the dimensions, frame rate, duration, and the root component of your video.

## The `<Composition />` Component

Use the `<Composition />` component to register a video.

```tsx
import { Composition } from 'motionforge';
import { MyVideo } from './MyVideo';

export const Root = () => {
return (
<Composition
id="MyVideo"
component={MyVideo}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
defaultProps={{
title: "Welcome to MotionForge"
}}
/>
);
};
```

### Props

| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `id` | `string` | **Required** | A unique identifier for the composition. |
| `component` | `React.ComponentType` | **Required** | The root React component to render. |
| `durationInFrames` | `number` | **Required** | Total number of frames in the video. |
| `fps` | `number` | `30` | Frames per second. |
| `width` | `number` | `1920` | Width of the video in pixels. |
| `height` | `number` | `1080` | Height of the video in pixels. |
| `defaultProps` | `Record<string, any>` | `{}` | Default props passed to the component. |
| `inputProps` | `Record<string, any>` | `{}` | Dynamic props that override defaults. |
| `calculateMetadata` | `Function` | `undefined` | Async function to resolve metadata dynamically. |
| `schema` | `ZodSchema` | `undefined` | Optional Zod schema for prop validation. |

---

## The `<Still />` Component

A convenience component for rendering a single frame (e.g., for thumbnails).

```tsx
import { Still } from 'motionforge';

<Still
id="Thumbnail"
component={MyVideo}
width={1280}
height={720}
defaultProps={{ frame: 45 }}
/>
```

Equivalent to a `Composition` with `durationInFrames={1}` and `fps={1}`.
52 changes: 52 additions & 0 deletions docs/data-and-async/delay-render.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# delayRender / continueRender

MotionForge provides a synchronization protocol to handle async operations like data fetching or asset loading during the rendering process.

## Why Use It?

The renderer needs to know when a frame is "ready" to be captured. If your component fetches data from an API, the renderer might capture the frame before the data arrives, resulting in a blank or incomplete video.

## Usage

Use `delayRender()` to signal an async operation and `continueRender()` when it's done.

```tsx
import { useEffect, useState } from 'react';
import { delayRender, continueRender, AbsoluteFill } from 'motionforge';

export const MyAsyncComponent = () => {
const [data, setData] = useState(null);

useEffect(() => {
// 1. Signal that we're starting an async operation
const handle = delayRender('Fetching user data');

fetch('/api/user')
.then(res => res.json())
.then(json => {
setData(json);
// 2. Signal that the operation is complete
continueRender(handle);
})
.catch(err => {
console.error(err);
// 3. Always continue, even on error!
continueRender(handle);
});
}, []);

if (!data) return null;

return (
<AbsoluteFill>
<h1>Hello, {data.name}!</h1>
</AbsoluteFill>
);
};
```

### Important Rules

1. **Always call `continueRender`**: If you forget, the renderer will wait until the safety timeout (30 seconds) before failing.
2. **Multiple calls**: You can call `delayRender` multiple times. The renderer will wait until **all** handles have been cleared via `continueRender`.
3. **Outside of React**: These functions are standalone and can be used in utility functions or outside of components.
Loading
Loading