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
12 changes: 0 additions & 12 deletions dev_server.log

This file was deleted.

2 changes: 1 addition & 1 deletion packages/create-motionforge/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-motionforge",
"version": "1.0.0",
"version": "1.1.0",
"description": "CLI tool to bootstrap MotionForge projects",
"bin": {
"create-motionforge": "./bin/index.js"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import React from 'react';
import { Player, AbsoluteFill, useCurrentFrame, interpolate, spring, useVideoConfig } from 'motionforge';
import { Player, AbsoluteFill, useCurrentFrame, interpolate, spring, useVideoConfig, LottieAnimation } from 'motionforge';

const HelloWorldComposition = () => {
const frame = useCurrentFrame();
Expand All @@ -20,7 +20,7 @@ const HelloWorldComposition = () => {
const translateY = interpolate(frame, [0, 30], [50, 0]);

return (
<AbsoluteFill className="bg-gradient-to-br from-emerald-900 to-black flex items-center justify-center">
<AbsoluteFill className="bg-gradient-to-br from-emerald-950 to-black flex items-center justify-center">
<div
style={{
opacity,
Expand All @@ -30,6 +30,16 @@ const HelloWorldComposition = () => {
>
<h1 className="text-7xl text-emerald-400 font-black mb-4">Hello MotionForge!</h1>
<p className="text-2xl text-emerald-600">Your programmatic video journey starts here.</p>

{/* Lottie Example: Clean, deterministic, and frame-synced */}
<div className="flex justify-center mt-12">
<div className="w-48 h-48 bg-emerald-500/10 rounded-full flex items-center justify-center p-4 border border-emerald-500/20 shadow-[0_0_50px_-12px_rgba(16,185,129,0.3)]">
<LottieAnimation
src="https://assets10.lottiefiles.com/packages/lf20_m6cu96.json"
loop
/>
Comment on lines +37 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

External URL in a starter template is a reliability risk.

The src points to https://assets10.lottiefiles.com/packages/lf20_m6cu96.json, which is a third-party CDN. If this asset is removed or the CDN is unreachable, new users will see a broken animation on first run — a poor out-of-box experience.

Consider bundling a local sample JSON (similar to the sample-lottie.json already added in this PR for the demo) and referencing it via a static file path or import.

Proposed fix: use a bundled local asset
+import sampleLottie from './sample-lottie.json';
 ...
             <LottieAnimation
-              src="https://assets10.lottiefiles.com/packages/lf20_m6cu96.json"
+              src={sampleLottie}
               loop
             />

You'd also need to include a copy of sample-lottie.json (or similar) in the template directory.

🤖 Prompt for AI Agents
In `@packages/create-motionforge/templates/hello-world/page.tsx.template` around
lines 37 - 40, The LottieAnimation usage currently points to an external CDN
URL; replace that with a bundled local JSON (e.g., sample-lottie.json) and
update the LottieAnimation src to reference the local asset via an import or
static file path instead of "https://assets10.lottiefiles.com/..."; add the
sample-lottie.json to the template directory so new projects ship with the asset
and update the component usage (LottieAnimation src prop) to load the local
file.

</div>
</div>
</div>
</AbsoluteFill>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
"lint": "next lint"
},
"dependencies": {
"motionforge": "^1.2.0",
"motionforge": "^1.3.0",
"next": "15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"framer-motion": "^11.13.1",
"lucide-react": "^0.468.0"
"lucide-react": "^0.468.0",
"lottie-web": "^5.12.2"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Version mismatch with motionforge's own lottie-web dependency.

The template pins lottie-web at ^5.12.2, but motionforge@^1.3.0 declares a dependency on lottie-web@^5.13.0. This mismatch could cause duplicate installations or resolution conflicts. Align both to the same version range.

🤖 Prompt for AI Agents
In `@packages/create-motionforge/templates/shared/package.json.template` at line
18, The template pins lottie-web to ^5.12.2 which conflicts with
motionforge@^1.3.0's lottie-web@^5.13.0 requirement; update the lottie-web entry
in the package.json.template from "^5.12.2" to "^5.13.0" (or match motionforge's
range) so both dependencies use the same version range and avoid duplicate
installations.

},
"devDependencies": {
"@types/node": "^20",
Expand Down
14 changes: 14 additions & 0 deletions packages/motionforge/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to MotionForge will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.0] - 2024-02-25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect date: 2024-02-25 should likely be 2026-02-15 (or the intended release date).

The changelog entry date appears to be two years in the past. The PR was created on 2026-02-15.

Proposed fix
-## [1.3.0] - 2024-02-25
+## [1.3.0] - 2026-02-15
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [1.3.0] - 2024-02-25
## [1.3.0] - 2026-02-15
🤖 Prompt for AI Agents
In `@packages/motionforge/CHANGELOG.md` at line 8, Update the incorrect release
date in the CHANGELOG entry header for version "## [1.3.0]" by replacing
"2024-02-25" with the intended release date "2026-02-15" so the changelog
reflects the PR creation/release date; edit the line that reads '## [1.3.0] -
2024-02-25' to '## [1.3.0] - 2026-02-15'.


### Added
- Production-grade Lottie animation support with `<LottieAnimation />`
- Frame-perfect synchronization between Lottie and MotionForge timeline
- Deterministic Lottie rendering (disabled internal requestAnimationFrame)
- Support for relative frame contexts in `Sequence`
- New Lottie demo and sample assets
- Lottie support in `create-motionforge` CLI templates

### Changed
- Updated `useRelativeCurrentFrame` to support `null` as default value for better sequence detection
- Improved CLI template to include `lottie-web` by default

## [1.2.0] - 2024-01-15

### Added
Expand Down
44 changes: 43 additions & 1 deletion packages/motionforge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- 🎬 **Frame-Based Rendering** - Precise control over every frame
- 🎨 **70+ Effect Components** - Fade, Scale, Slide, 3D transforms, particles, and more
- 🌊 **Spring Physics** - Natural, physics-based animations
- 🎬 **Lottie Support** - Full integration with Lottie-web for vector animations
- 📊 **Interpolation System** - Smooth transitions with 20+ easing functions
- 🎮 **Interactive Player** - Real-time preview with timeline controls
- 📦 **Frame Caching** - LRU cache for optimized performance
Expand All @@ -42,6 +43,8 @@ bun add motionforge

## 🚀 Quick Start

### Basic Animation

```tsx
import {
AbsoluteFill,
Expand Down Expand Up @@ -87,8 +90,38 @@ const MyVideo = () => {
</AbsoluteFill>
);
};
```

### Using Lottie

MotionForge provides first-class support for Lottie animations, fully synchronized with the frame system.

```tsx
import { LottieAnimation } from 'motionforge';
import myAnimation from './animation.json';

const MyComp = () => {
return (
<LottieAnimation
src={myAnimation}
loop
playbackRate={1}
/>
);
};
```

**Properties for `<LottieAnimation />`:**
- `src`: JSON object or URL to the Lottie file.
- `frameStart`: Frame to start from (default: 0).
- `frameEnd`: Frame to end at (default: animation end).
- `playbackRate`: Speed of playback (default: 1).
- `loop`: Whether to loop the animation (default: false).
- `width`/`height`: Dimensions of the container.

// Use with Player
### Player Integration

```tsx
const App = () => (
<Player
component={MyVideo}
Expand Down Expand Up @@ -116,6 +149,15 @@ const App = () => (
| `Reverse` | Play content backwards |
| `Series` | Sequential scene management |

### Media Components

| Component | Description |
|-----------|-------------|
| `Video` | Frame-synced video playback |
| `Audio` | Frame-synced audio playback |
| `Img` | Static image component |
| `LottieAnimation` | Production-grade, frame-synced Lottie animations |

### Effect Components

```tsx
Expand Down
6 changes: 5 additions & 1 deletion packages/motionforge/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/motionforge/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "motionforge",
"version": "1.2.0",
"version": "1.3.0",
"description": "A React-based framework for creating videos programmatically. Build stunning videos with React components, spring animations, and frame-perfect control.",
"author": "MotionForge Team",
"license": "MIT",
Expand Down Expand Up @@ -87,5 +87,8 @@
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"dependencies": {
"lottie-web": "^5.13.0"
}
Comment on lines +91 to 93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the motionforge package.json dependencies
cat -n packages/motionforge/package.json | grep -A 5 -B 5 "lottie-web"

Repository: codedbytahir/motionforge

Length of output: 272


🏁 Script executed:

# Check the CLI template for lottie-web version
cat -n shared/package.json.template | grep -A 5 -B 5 "lottie-web"

Repository: codedbytahir/motionforge

Length of output: 128


🏁 Script executed:

# Find if lottie-web or LottieAnimation is used in the codebase
rg -i "lottie" --type ts --type tsx --type js --type jsx

Repository: codedbytahir/motionforge

Length of output: 95


🌐 Web query:

latest version of lottie-web npm package 2025

💡 Result:

The latest lottie-web version on npm released in 2025 is 5.13.0 (it’s also the current latest tag), published ~4 months ago (≈ Oct 2025). [1]

Source: npm package page for lottie-web@5.13.0. [1]

References:
[1] (npmjs.com)

Citations:


🏁 Script executed:

# Search for lottie-web usage without type restrictions
rg "lottie-web" -A 3 -B 1

Repository: codedbytahir/motionforge

Length of output: 3841


🏁 Script executed:

# Search for lottie or LottieAnimation more broadly
rg "lottie" -i -A 2 -B 1

Repository: codedbytahir/motionforge

Length of output: 22590


🏁 Script executed:

# Find if there's a template file with a different name/path
fd "package.*template" -type f

Repository: codedbytahir/motionforge

Length of output: 238


🏁 Script executed:

# List files in shared directory to see what exists
ls -la shared/ 2>/dev/null || echo "shared directory not found"

Repository: codedbytahir/motionforge

Length of output: 94


Update the CLI template to use lottie-web ^5.13.0 to match the main package.

The template at packages/create-motionforge/templates/shared/package.json.template specifies lottie-web ^5.12.2, but packages/motionforge/package.json requires ^5.13.0. These versions don't overlap (^5.12.2 is >=5.12.2 <5.13.0, while ^5.13.0 is >=5.13.0 <5.14.0), which can cause version conflicts when users scaffold a project with the template and install motionforge. Update the template to ^5.13.0.

Regarding the optional dependency suggestion: LottieAnimation is a core, directly-exported component with a top-level import of lottie-web. Making it optional would require dynamic imports and error handling throughout the codebase, which is a significant refactor. Since LottieAnimation is a first-class feature (not a conditional enhancement), keeping it as a hard dependency is reasonable.

🤖 Prompt for AI Agents
In `@packages/motionforge/package.json` around lines 91 - 93, Update the shared
package.json.template to make the lottie-web dependency match the main package:
change the "lottie-web" version specifier from "^5.12.2" to "^5.13.0" in the
create-motionforge CLI template (the shared package.json.template) so newly
scaffolded projects use a compatible lottie-web range that matches the
packages/motionforge package.json.

}
127 changes: 127 additions & 0 deletions packages/motionforge/src/components/Lottie.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
'use client';

import React, { useEffect, useRef, useState } from 'react';
import lottie, { AnimationItem, AnimationConfigWithData, AnimationConfigWithPath } from 'lottie-web';
import { useCurrentFrame, useVideoConfig } from '../core/context';
import { useRelativeCurrentFrame } from './Sequence';
import { LottieAnimationProps } from '../core/types';

/**
* LottieAnimation Component
*
* Renders a Lottie animation synchronized with the MotionForge frame system.
* This component is deterministic and does not use requestAnimationFrame for playback.
*/
export const LottieAnimation: React.FC<LottieAnimationProps> = ({
src,
frameStart = 0,
frameEnd,
playbackRate = 1,
loop = false,
width,
height,
style,
className,
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const animationRef = useRef<AnimationItem | null>(null);

// Use relative frame if inside a Sequence, otherwise use absolute frame
const relativeFrame = useRelativeCurrentFrame();
const absoluteFrame = useCurrentFrame();
const currentFrame = relativeFrame !== null ? relativeFrame : absoluteFrame;

const [isLoaded, setIsLoaded] = useState(false);

// Initialize Lottie instance
useEffect(() => {
if (!containerRef.current) return;

// Clear container to prevent duplicate renders
containerRef.current.innerHTML = '';

const commonConfig = {
container: containerRef.current,
renderer: 'svg' as const,
loop: false, // We control playback manually
autoplay: false, // We control playback manually
};

let anim: AnimationItem;

if (typeof src === 'string') {
anim = lottie.loadAnimation({
...commonConfig,
path: src,
} as AnimationConfigWithPath);
} else {
// Create a deep copy of animationData to prevent lottie-web from mutating it
anim = lottie.loadAnimation({
...commonConfig,
animationData: JSON.parse(JSON.stringify(src)),
} as AnimationConfigWithData);
}

animationRef.current = anim;

const handleLoaded = () => {
setIsLoaded(true);
};

// Lottie emits DOMLoaded when the SVG elements are added to the DOM
anim.addEventListener('DOMLoaded', handleLoaded);

return () => {
anim.removeEventListener('DOMLoaded', handleLoaded);
anim.destroy();
animationRef.current = null;
};
}, [src]);

// Synchronize Lottie frame with MotionForge frame
useEffect(() => {
if (!animationRef.current || !isLoaded) return;

const anim = animationRef.current;

// totalFrames is the number of frames in the Lottie animation
const totalLottieFrames = anim.totalFrames;

// Determine the effective end frame for the animation window
const end = frameEnd !== undefined ? frameEnd : totalLottieFrames;
const duration = end - frameStart;

// Calculate the target frame in the Lottie animation
let lottieFrame = (currentFrame * playbackRate) + frameStart;

if (loop && duration > 0) {
// Loop within the specified window [frameStart, end]
lottieFrame = frameStart + ((lottieFrame - frameStart) % duration);
} else {
// Clamp to the end frame if not looping
lottieFrame = Math.min(lottieFrame, end);
}

// Go to the calculated frame and stop (prevent internal playback)
// The second parameter 'true' indicates we are using frame numbers, not time.
anim.goToAndStop(lottieFrame, true);
}, [currentFrame, isLoaded, frameStart, frameEnd, playbackRate, loop]);

return (
<div
ref={containerRef}
className={className}
style={{
width: width || '100%',
height: height || '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
...style,
}}
/>
);
};

export default LottieAnimation;
7 changes: 1 addition & 6 deletions packages/motionforge/src/components/Media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { interpolate } from '../utils/animation';

// Absolute Fill - Container component
interface AbsoluteFillProps {
children: React.ReactNode;
children?: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
Expand Down Expand Up @@ -223,11 +223,6 @@ export const Img: React.FC<ImgProps> = ({
};

// StaticFile component - for local static assets
interface StaticFileProps {
src: string;
style?: React.CSSProperties;
}

export const staticFile = (path: string): string => {
// In a real implementation, this would resolve to a static file path
return `/static/${path}`;
Expand Down
2 changes: 1 addition & 1 deletion packages/motionforge/src/components/Sequence.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const SequenceFrameProvider: React.FC<SequenceFrameProviderProps> = ({
};

// Relative Frame Context
const RelativeFrameContext = createContext<number>(0);
const RelativeFrameContext = createContext<number | null>(null);

export const useRelativeCurrentFrame = () => useContext(RelativeFrameContext);
Comment on lines +110 to 112
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the Sequence.tsx file around the lines mentioned
cat -n packages/motionforge/src/components/Sequence.tsx | sed -n '100,125p'

Repository: codedbytahir/motionforge

Length of output: 946


🏁 Script executed:

# Search for all usages of useRelativeCurrentFrame
rg -n 'useRelativeCurrentFrame' --type=ts --type=tsx -B2 -A4

Repository: codedbytahir/motionforge

Length of output: 95


🏁 Script executed:

# Also search in JS/JSX files in case there are any
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) | xargs grep -l 'useRelativeCurrentFrame' 2>/dev/null | head -20

Repository: codedbytahir/motionforge

Length of output: 391


🏁 Script executed:

# Check the Lottie.tsx file where useRelativeCurrentFrame is likely used
cat -n packages/motionforge/src/components/Lottie.tsx

Repository: codedbytahir/motionforge

Length of output: 4820


🏁 Script executed:

# Check the index.ts files to see how it's exported
cat -n packages/motionforge/src/index.ts

Repository: codedbytahir/motionforge

Length of output: 5664


🏁 Script executed:

# Get more detailed context around usages
rg 'useRelativeCurrentFrame' --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -B3 -A3 packages/motionforge/src/ 2>/dev/null || rg 'useRelativeCurrentFrame' -B3 -A3 packages/motionforge/src/

Repository: codedbytahir/motionforge

Length of output: 2347


🏁 Script executed:

# Check if there are other usages in the src/lib/remotion directories
cat -n src/lib/remotion/components/Lottie.tsx 2>/dev/null | head -50

Repository: codedbytahir/motionforge

Length of output: 2005


🏁 Script executed:

# Check the other Sequence file
cat -n src/lib/remotion/components/Sequence.tsx 2>/dev/null | head -50

Repository: codedbytahir/motionforge

Length of output: 1667


🏁 Script executed:

# Search for any other usages in the entire src directory
rg 'useRelativeCurrentFrame' src/ 2>/dev/null || find src -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -exec grep -l 'useRelativeCurrentFrame' {} \;

Repository: codedbytahir/motionforge

Length of output: 423


useRelativeCurrentFrame type change properly handled internally.

The type change from number to number | null is a breaking change for external consumers, but internal usage in LottieAnimation correctly handles it with a null check: relativeFrame !== null ? relativeFrame : absoluteFrame. However, external consumers of this exported hook may not handle the null case—document this change in release notes and consider providing a migration guide.

🤖 Prompt for AI Agents
In `@packages/motionforge/src/components/Sequence.tsx` around lines 110 - 112, The
exported hook useRelativeCurrentFrame now returns number | null
(RelativeFrameContext), which is a breaking change for external consumers—update
the release notes and add a short migration guide showing how to handle the null
case (e.g., using relativeFrame !== null ? relativeFrame : absoluteFrame, as
used in LottieAnimation) and include a minimal example snippet and suggested
fallback strategy; also mention the symbols RelativeFrameContext,
useRelativeCurrentFrame, LottieAnimation, relativeFrame and absoluteFrame so
consumers can find the relevant code, and optionally document or add a helper
alternative hook for callers that want a guaranteed number fallback.


Expand Down
12 changes: 12 additions & 0 deletions packages/motionforge/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ export interface TextProps {
children: React.ReactNode;
}

export interface LottieAnimationProps {
src: string | object;
frameStart?: number;
frameEnd?: number;
playbackRate?: number;
loop?: boolean;
width?: number | string;
height?: number | string;
style?: React.CSSProperties;
className?: string;
}

export interface SpringConfig {
frame: number;
fps: number;
Expand Down
Loading