Summary
From the zoom-rendering audit (Workspace CC). Architectural improvement that's only realistic to do AFTER the Rust core port (#6) lands.
What the audit found
animateCamera uses requestAnimationFrame (CanvasView.tsx:356). 18 bridge crossings × 3 shared-value writes ≈ 54 round-trips per 300ms animation. Reanimated worklets would do this all on the UI thread, no bridge — but withTiming, withSpring, and useFrameCallback all corrupted shared values to ~10^16 on Fabric + react-native-macos (called out in the same comment block: "the reanimated animation queue corrupts shared values on Fabric + react-native-macos"). The JS-RAF approach is the workaround.
Why this is Rust-port-blocked
In the Rust-backed renderer (#6), the camera mutation pipeline is owned end-to-end by Rust:
- JSI / wasm-bindgen exposes
set_camera(tx, ty, scale) on a native handle
- A native interpolator (running on the UI thread on iOS / Android / macOS, or in a wasm worker on web) drives the camera matrix per-frame
- No bridge crossings; no Reanimated dependency for this code path
This isn't an improvement of the current code — it's a different architecture that the Rust port enables. Filing as a Rust-port follow-up so it doesn't get lost.
Proposed fix shape
After #6 lands and src/core/ is Rust-backed:
- Add a
camera::AnimateTo { to_tx, to_ty, to_scale, duration_ms, easing } operation to the Rust core's API
- Native interpolator runs the tick loop in Rust, calling a JSI callback (or Reanimated SharedValue write) once per frame from the UI thread
- JS-side
animateCamera becomes a thin shim that calls into the Rust API
- Drop the
requestAnimationFrame path; the animCancelled SharedValue flag becomes Rust-side state
Acceptance criteria
Depends on
Notes
Until #6 lands, this is filed for tracking only. No work to do now.
Refs
Surfaced during the zoom-rendering improvements audit (Workspace CC).
Summary
From the zoom-rendering audit (Workspace CC). Architectural improvement that's only realistic to do AFTER the Rust core port (#6) lands.
What the audit found
animateCamerausesrequestAnimationFrame(CanvasView.tsx:356). 18 bridge crossings × 3 shared-value writes ≈ 54 round-trips per 300ms animation. Reanimated worklets would do this all on the UI thread, no bridge — butwithTiming,withSpring, anduseFrameCallbackall corrupted shared values to ~10^16 on Fabric +react-native-macos(called out in the same comment block: "the reanimated animation queue corrupts shared values on Fabric + react-native-macos"). The JS-RAF approach is the workaround.Why this is Rust-port-blocked
In the Rust-backed renderer (#6), the camera mutation pipeline is owned end-to-end by Rust:
set_camera(tx, ty, scale)on a native handleThis isn't an improvement of the current code — it's a different architecture that the Rust port enables. Filing as a Rust-port follow-up so it doesn't get lost.
Proposed fix shape
After #6 lands and
src/core/is Rust-backed:camera::AnimateTo { to_tx, to_ty, to_scale, duration_ms, easing }operation to the Rust core's APIanimateCamerabecomes a thin shim that calls into the Rust APIrequestAnimationFramepath; theanimCancelledSharedValue flag becomes Rust-side stateAcceptance criteria
animCancelledsemantics preserved (pinch / pan during animation cancels cleanly)Depends on
Notes
Until #6 lands, this is filed for tracking only. No work to do now.
Refs
Surfaced during the zoom-rendering improvements audit (Workspace CC).