Skip to content

Conversation

@mattgperry
Copy link
Collaborator

@mattgperry mattgperry commented Jan 7, 2026

Summary

  • Add comprehensive documentation of the projection system architecture, identified performance issues, and potential improvements
  • Export projection types from framer-motion/projection

Key Findings

Object Allocation Strategy

Microbenchmarks show object pooling is 30-95% slower than V8's native allocation due to pool overhead. The real issue is GC pauses during animation, not allocation speed.

Recommendation: Allocate at animation start and retain on nodes, don't pool. The critical issue is resolveTargetDelta() calling applyTransform() every frame during shared layout animations (line 1253-1255).

Cumulative Transform Caching

Storing cumulative scroll/transforms at each node (O(1) lookup) instead of iterating the path array (O(depth)) shows 50-73% improvement potential, scaling with tree depth.

Files Changed

  • packages/framer-motion/src/projection/README.md - Comprehensive documentation
  • packages/framer-motion/package.json - Add types for projection export
  • packages/framer-motion/rollup.config.mjs - Generate projection.d.ts

Test plan

  • Run yarn build - passes
  • Run yarn test in packages/framer-motion - passes

🤖 Generated with Claude Code

@claude
Copy link

claude bot commented Jan 7, 2026

Code Review: Projection System Analysis and Benchmarks

Thank you for this comprehensive PR! This is excellent work documenting and benchmarking the projection system. Here's my detailed review:


✅ Strengths

1. Outstanding Documentation

The packages/framer-motion/src/projection/README.md is exceptionally thorough:

  • Clear architecture overview with directory structure
  • Well-explained data structures and update cycle phases
  • Comprehensive identification of performance bottlenecks with line numbers
  • Concrete, actionable improvement recommendations

2. Scientific Approach to Performance

The key finding about object pooling being 30-95% slower than V8's native allocation is valuable and counterintuitive. This demonstrates proper benchmarking methodology - testing assumptions rather than following conventional wisdom.

3. Real Metrics Integration

Using recordStats() from the actual projection system in the React benchmarks provides authentic performance data rather than synthetic tests. This is the right approach.

4. Proper Export Configuration

Adding TypeScript types to the framer-motion/projection export in package.json follows the existing package structure correctly.


🔍 Code Quality Observations

React Benchmarks (dev/react/src/examples/)

Good practices:

  • Clean component structure with proper separation of concerns
  • Proper cleanup in useEffect return functions
  • Handling of React StrictMode double-invocation with try-catch blocks
  • Clear visual feedback while waiting for stats

Minor suggestions:

  1. Type safety (Benchmark-projection-stress.tsx:29, :74):
// Current
function StatsDisplay({ stats }: { stats: any }) {

// Consider defining an interface
interface ProjectionStats {
  frameloop: {
    rate: { avg: number; min: number; max: number }
    preRender: { avg: number }
    render: { avg: number }
  }
  animations: {
    layout: { avg: number }
    mainThread: { avg: number }
  }
  layoutProjection: {
    nodes: { avg: number }
    calculatedTargetDeltas: { avg: number }
    calculatedProjections: { avg: number }
  }
}
  1. Magic numbers (all benchmark files):
    The durations and delays are hardcoded. Consider extracting to constants at the top:
const ANIMATION_DURATION = 3
const STATS_COLLECTION_CYCLES = 3
const CYCLE_BUFFER_MS = 100
const statsTimeout = (ANIMATION_DURATION * 1000 + CYCLE_BUFFER_MS) * STATS_COLLECTION_CYCLES
  1. Error handling (Benchmark-projection-stress.tsx:100):
    The error logging is good, but consider informing the user visually when stats collection fails:
} catch (e) {
    console.error("Error getting stats:", e)
    setStats({ error: true }) // Show error state in UI
}

HTML Benchmarks (dev/html/public/benchmarks/)

Observations:

  1. Code duplication: The simulation classes (SimNode) across different HTML files share significant logic. While these are test files, consider extracting common simulation utilities to reduce maintenance burden.

  2. Performance measurement accuracy: Using performance.now() for timing is correct, but be aware that microbenchmarks can be affected by:

    • Browser JIT warm-up
    • Garbage collection cycles
    • Background tasks

    The current approach of running continuous iterations helps mitigate this.

  3. Memory profiling: The Chrome-only performance.memory check is good (projection-allocations.html:428-438), but consider adding a note in the UI when running in non-Chrome browsers.


🐛 Potential Issues

1. StrictMode Memory Leak Risk (All React benchmarks)

The current cleanup might not handle all edge cases in StrictMode:

useEffect(() => {
    try {
        reportStatsRef.current = recordStats()
    } catch {
        // Stats already being measured, ignore
    }
    // ...
    return () => {
        if (reportStatsRef.current) {
            try {
                reportStatsRef.current()
            } catch {
                // Already stopped
            }
        }
    }
}, [])

Issue: If StrictMode causes the first recordStats() to succeed but the cleanup doesn't run before the second mount, you could have a reference leak.

Suggestion: Use a more robust guard:

const isRecordingRef = useRef(false)

useEffect(() => {
    if (isRecordingRef.current) return
    
    try {
        reportStatsRef.current = recordStats()
        isRecordingRef.current = true
    } catch {
        // Stats already being measured
    }
    
    return () => {
        if (reportStatsRef.current && isRecordingRef.current) {
            try {
                reportStatsRef.current()
            } finally {
                isRecordingRef.current = false
            }
        }
    }
}, [])

2. Missing Rollup Export (rollup.config.mjs)

The PR adds projectionTypes constant but I don't see it exported in the default array. This would cause the types to not be built.

Verification needed: Check if projectionTypes is added to the export array around line 191+.


🚀 Performance Considerations

Critical Finding Validation

The documentation identifies resolveTargetDelta() calling applyTransform() every frame (line 1253-1255) as the most critical issue. This assessment appears correct based on:

  1. It's in the hot path (called every animation frame)
  2. It allocates a new Box object per call
  3. It happens during shared layout animations (common use case)

Recommendation: This should be the first optimization to implement in a follow-up PR.

Benchmark Coverage

The benchmarks cover the key scenarios:

  • ✅ Stress test (200 nodes)
  • ✅ Deep trees (O(depth) operations)
  • ✅ Shared layouts (layoutId transitions)
  • ✅ Nested scroll containers

Good coverage, though consider adding:

  • Mixed scenario: combination of shared layout + deep nesting + scroll
  • Scale test: progressively increasing node counts (100, 500, 1000, 2000) to identify non-linear performance degradation

🔒 Security Concerns

No security issues identified. The benchmarks are development-only and don't:

  • Accept user input
  • Make network requests
  • Execute arbitrary code
  • Access sensitive APIs

✅ Test Coverage

Current State

The PR adds benchmarks but no unit tests. This is appropriate because:

  • This PR is analysis/documentation focused
  • Benchmarks serve as integration tests for performance characteristics
  • No production code is modified (only exports added)

For Follow-up PRs

When implementing the optimizations described in the README:

  1. Add unit tests for any new functions (e.g., applyTransformInto)
  2. Add regression tests to ensure optimizations don't break existing behavior
  3. Use the benchmarks to validate performance improvements

📝 Documentation Quality

The README.md is production-ready with minor suggestions:

  1. Add a table of contents for easier navigation (it's 650+ lines)
  2. Version the findings: Note which version of framer-motion was analyzed
  3. Link to benchmarks: Add direct links to the benchmark files from relevant sections
  4. Diagrams: Consider adding a visual diagram of the update cycle phases

🎯 Recommendations

Before Merge

  1. ✅ Verify projectionTypes is exported in rollup config
  2. ✅ Fix StrictMode edge case in React benchmarks
  3. ✅ Add stats.error handling in UI
  4. ✅ Replace any types with proper interfaces

Post-Merge (Follow-up PRs)

  1. High Priority: Implement the resolveTargetDelta optimization (biggest impact)
  2. Medium Priority: Cumulative transform caching (50-73% improvement shown)
  3. Low Priority: String building optimization (minor impact)

Testing Checklist

Before merge, please verify:

  • yarn build passes (including projection types)
  • yarn test passes in packages/framer-motion
  • Benchmarks load without errors at http://localhost:9990
  • Stats display correctly after the wait period
  • No console errors in browser dev tools

Summary

This is excellent analytical work that will serve as a valuable foundation for optimizing the projection system. The scientific approach, comprehensive documentation, and real-world benchmarks demonstrate high-quality engineering.

Recommendation: ✅ Approve with minor changes

The suggested fixes are small and non-blocking. The value this PR provides in terms of documentation and benchmarking infrastructure far outweighs the minor issues noted.

Great work! 🎉

@mattgperry mattgperry force-pushed the projection-optimisations branch from 6ed57fc to 71a9ab9 Compare January 7, 2026 05:38
@claude
Copy link

claude bot commented Jan 7, 2026

Code Review - PR #3458: Projection System Analysis and Benchmarks

Thank you for this comprehensive documentation and analysis of the projection system! This is an excellent contribution that will help both maintainers and contributors understand this complex subsystem. Here's my detailed review:


✅ Strengths

Documentation Quality

  • Exceptional depth and clarity: The README provides a complete mental model of the projection system, from data structures to update cycles
  • Well-structured: Clear progression from architecture → data flow → performance issues → improvements
  • Code references: Specific line numbers make it easy to cross-reference with actual implementation
  • Visual aids: ASCII diagrams and tables enhance understanding
  • Benchmarking approach: Mentions 4 React benchmarks with real metrics (though benchmark files don't appear to be included in this PR)

Technical Analysis

  • Evidence-based insights: The finding that object pooling is 30-95% slower than V8's native allocation is valuable and counterintuitive
  • Correct problem identification: Focusing on GC pauses during animation rather than allocation speed shows deep understanding
  • Algorithmic complexity analysis: Clear identification of O(depth) and O(n × depth) issues
  • Concrete solutions: Each problem has actionable improvement suggestions with code examples

🔍 Issues & Concerns

1. Missing Benchmark Files (Critical)

The PR description mentions 4 benchmarks (Benchmark-projection-stress, Benchmark-projection-deep-tree, etc.) but they're not included in the changed files:

packages/framer-motion/package.json
packages/framer-motion/rollup.config.mjs  
packages/framer-motion/src/projection/README.md

Question: Are the benchmark files intended to be in a separate PR, or were they accidentally omitted?

2. Missing Type Definition File

The changes add a new export entry point:

"./projection": {
    "types": "./dist/projection.d.ts",  // ← New
    "import": "./dist/es/projection.mjs",
    "default": "./dist/es/projection.mjs"
}

And configure rollup to generate it:

const projectionTypes = createTypes("types/projection.d.ts", "dist/projection.d.ts")

However, packages/framer-motion/types/projection.d.ts doesn't exist yet. This will likely cause the build to fail.

Recommended fix: Create packages/framer-motion/types/projection.d.ts that re-exports from the source:

export * from '../src/projection'

3. Build Verification Needed

The test plan says "Run yarn build - passes" but this should be verified in CI. The missing types file will cause build failures.


💡 Suggestions

Documentation Improvements

1. Add benchmark methodology section
Since benchmarks are mentioned as key evidence, document:

  • How to run them
  • What metrics to look for
  • How to interpret results
  • Baseline vs. optimized comparisons

2. Clarify coordinate system diagram (lines 213-225)
The "Current Flow (problematic)" section could benefit from a concrete example:

// Example: Element at viewport (100, 100), scrolled 50px down
// Step 1: measureViewportBox() → {x: {min: 100, max: 200}, y: {min: 100, max: 200}}
// Step 2: + root scroll → {x: {min: 100, max: 200}, y: {min: 150, max: 250}}
// ...

3. Add "Quick Start" section
For developers new to the projection system, add a "TL;DR" at the top:

## Quick Start
- **What**: FLIP animation system for layout changes
- **When to read this**: Working on layout animations, investigating performance
- **Key files**: create-projection-node.ts (~2360 lines), geometry/*, styles/*
- **Main issues**: Per-frame allocations (#1), O(depth) iterations (#2)

4. Prioritize improvements
Add a priority ranking to the improvements section:

## Recommended Implementation Order
1. **High Impact, Low Risk**: #1 (Retain objects), #2 (Cumulative transforms)
2. **Medium Impact, Medium Risk**: #3 (Coordinate system), #7 (Transform building)
3. **High Risk, Needs Design**: #4 (Two-pass resolution), #9 (Web Workers)

Code Quality

5. TypeScript interface examples (lines 92-123, 489-503)
The example interfaces show proposed changes but aren't marked as pseudocode. Add markers:

// PROPOSED: New fields for cumulative transform caching
interface IProjectionNode {
    cumulativeScale: Point
    // ...
}

6. Line number maintenance
The document references specific line numbers extensively (e.g., "Line 1253-1255"). Consider:

  • Adding a note that line numbers are approximate and may shift
  • Using function names instead: resolveTargetDelta() method
  • Or both: resolveTargetDelta() (line ~1253)

Performance Considerations

7. Validate object pooling benchmarks
The claim that pooling is "30-95% slower" is surprising. Document:

  • Pool implementation tested (was it optimized?)
  • Benchmark methodology
  • Whether this applies to all object sizes or specific cases
  • V8 version and settings

This prevents future contributors from re-attempting pooling without context.

8. Memory retention trade-offs
Improvement #1 suggests retaining objects on nodes. Document the memory implications:

  • What happens with 10,000 nodes that animate once then stay static?
  • Should there be a cleanup mechanism for inactive nodes?
  • Memory vs. GC pause trade-off

🔒 Security Concerns

No security issues identified. This is documentation and internal API exports only.


🧪 Test Coverage

Missing:

  • Unit tests for the newly exported types
  • Integration test verifying framer-motion/projection imports work
  • The mentioned benchmark files

Recommended:

// packages/framer-motion/src/__tests__/projection-exports.test.ts
import { 
    HTMLProjectionNode, 
    calcBoxDelta, 
    nodeGroup 
} from 'framer-motion/projection'

test('projection exports are accessible', () => {
    expect(HTMLProjectionNode).toBeDefined()
    expect(calcBoxDelta).toBeDefined()
    expect(nodeGroup).toBeDefined()
})

📋 Action Items

Must Fix Before Merge:

  1. ✅ Create packages/framer-motion/types/projection.d.ts
  2. ✅ Verify build passes with new export configuration
  3. ❓ Clarify benchmark file status (separate PR or missing?)

Should Consider:

  1. Add Quick Start section to README
  2. Document benchmark methodology
  3. Add export test
  4. Mark example code as proposed/pseudocode
  5. Add priority ranking to improvements

Nice to Have:

  1. Add concrete examples to coordinate system section
  2. Document memory implications of object retention
  3. Add note about line number maintenance

Summary

This is excellent work that significantly improves the project's documentation. The technical analysis is thorough and the proposed optimizations are well-reasoned. The main blocker is the missing type definition file that will cause build failures. Once that's addressed and benchmarks are clarified, this will be a valuable addition to the codebase.

Recommendation: Request changes to add the missing types/projection.d.ts file, then approve after verification that the build passes.


Great job on this analysis! The insight about object pooling performance is particularly valuable. 🚀

@mattgperry mattgperry changed the title Projection system analysis and benchmarks Add projection system documentation and type exports Jan 7, 2026
- Add comprehensive README documenting projection system architecture,
  identified performance issues, and potential improvements
- Export projection types from framer-motion/projection

Key findings documented:
- Object pooling is slower than V8 allocation; retain objects on nodes instead
- Cumulative scroll/transform caching shows 50-73% improvement potential
- Critical per-frame allocation in resolveTargetDelta() with resumingFrom

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mattgperry mattgperry force-pushed the projection-optimisations branch from 71a9ab9 to bad8515 Compare January 7, 2026 05:47
@claude
Copy link

claude bot commented Jan 7, 2026

Pull Request Review

Overview

This PR adds comprehensive documentation for the projection system and exports projection types. The changes are well-structured and valuable for both maintainers and users of the library.

Code Quality ✅

Documentation (README.md)

Strengths:

  • Extremely thorough: 751 lines of detailed documentation covering architecture, data flow, performance issues, and potential improvements
  • Well-organized: Clear section hierarchy from overview → architecture → issues → improvements
  • Code examples: Excellent use of TypeScript code snippets to illustrate concepts
  • Performance insights: Evidence-based analysis with specific line references and microbenchmark results
  • Actionable: Each performance issue includes concrete improvement proposals

Specific highlights:

  • The FLIP animation explanation (lines 9-14) is clear and concise
  • Architecture diagram (lines 18-52) provides excellent navigation reference
  • Data structure definitions (lines 55-123) are well-documented with inline comments
  • Update cycle phases (lines 125-160) clearly explain the timing and purpose of each phase
  • Performance issue categorization (per-frame vs per-measurement vs one-time allocations) is very helpful

Minor suggestions:

  1. Consider adding a table of contents at the top for easier navigation of this large document
  2. The coordinate systems section (lines 205-225) might benefit from a diagram showing the relationships between viewport/page/parent-relative coordinates
  3. Consider adding complexity notation (Big-O) in section headers where relevant (e.g., "## 2. O(depth) Path Iterations")

Type Exports (package.json + rollup.config.mjs)

Strengths:

  • Correct implementation: Follows the exact pattern used for other exports (mini, dom, debug, m)
  • Proper placement: Types entry comes before import/default in package.json:50-54 (correct resolution order)
  • Consistency: Matches the existing createTypes pattern in rollup config

Verification:

  • ✅ package.json export maps ./projection with types field
  • ✅ rollup.config.mjs creates projectionTypes using the same pattern
  • ✅ Added to default export array (line 216)
  • ✅ Source file exists at src/projection.ts

Potential Issues ⚠️

1. Missing type source validation

The PR adds type generation config but doesn't verify that types/projection.d.ts will be generated correctly during build. Since the types/ directory appears to be gitignored, this should be tested.

Recommendation: Verify the build produces types/projection.d.ts and dist/projection.d.ts correctly:

yarn build
ls -la types/projection.d.ts
ls -la dist/projection.d.ts

2. Documentation location

The README.md is placed inside src/projection/ which means it will be included in npm package. This is likely intentional but worth confirming.

Question: Should this documentation also be referenced from the main repository README or docs site?

Performance Considerations 🚀

The documentation itself identifies critical performance issues:

Critical (should be prioritized):

  1. Per-frame allocations in shared layout animations (line 260-267): The resolveTargetDelta()applyTransform() call creates new Box objects every frame, causing GC pressure

High impact potential:

  1. Cumulative transform caching (line 368-382, improvement section 484-505): Could reduce O(n × depth) to O(n) - microbenchmarks show 50-73% improvement
  2. Coordinate system formalization (line 330-349, improvement section 507-660): Would eliminate redundant path iterations

The PR correctly notes that object pooling is counterproductive (30-95% slower than V8 allocation), which is an important finding.

Security Concerns 🔒

No security issues identified. This PR only adds:

  • Documentation (static content)
  • Type exports (no runtime code)
  • Build configuration (no execution)

Test Coverage ✅

The PR description indicates:

  • yarn build passes
  • yarn test in packages/framer-motion passes

Additional testing recommended:

  1. Verify TypeScript consumers can import from framer-motion/projection with proper type inference
  2. Test that the exported types match the actual runtime exports in src/projection.ts
  3. Verify tree-shaking works correctly (projection imports shouldn't pull in React code)

Example consumer test:

import { HTMLProjectionNode, calcBoxDelta } from 'framer-motion/projection'
// Should have full type information without errors

Best Practices Adherence ✅

Based on CLAUDE.md:

  • ✅ Follows existing package export patterns
  • ✅ Rollup config matches conventions (createTypes pattern)
  • ✅ No default exports
  • ✅ TypeScript with proper types

Summary

Verdict: ✅ Approve with minor recommendations

This is an excellent contribution that:

  1. Provides invaluable documentation for maintainers working on the projection system
  2. Correctly exports types following established patterns
  3. Includes evidence-based performance analysis with specific improvement proposals

Before merging:

  • Verify build generates type files correctly
  • Test consumer imports work with proper types
  • Consider adding the documentation to the main docs site for discoverability

Nice to have (future work):

  • Add table of contents to README.md
  • Create coordinate system diagram
  • Consider creating follow-up issues for the identified performance improvements

Great work! This documentation will be incredibly valuable for anyone working on or debugging the projection system. 🎉

@mattgperry mattgperry merged commit 4de5a59 into main Jan 7, 2026
1 of 4 checks passed
@mattgperry mattgperry deleted the projection-optimisations branch January 7, 2026 05:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants