motiondivision / motiondivision/motion
[BUG] Animations start late with Chrome DevTools playback at 10%
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 33.7k
- Forks
- 1.4k
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 14
Description
Motion 13.2.0 opacity animations can wait tens of seconds before starting when Chrome DevTools Animations playback is set to 10%. Once they start, they animate at the correct slowed speed. A native Web Animations control starts immediately and respects the same slowdown.
The failure reproduces with a permanently mounted element, without AnimatePresence, conditional rendering, keys, or entry/exit animations.
Reproduction
CodeSandbox source · Standalone preview
Open the standalone preview in its own tab for the DevTools playback test. It contains the Motion fade and a native Web Animations control driven by the same button.
React 18.3.1, motion 13.2.0, motion-dom 13.2.0. Automated inspection used Chrome reporting version 153.0.0.0 on macOS. Motion 13.2.0 was the npm latest release when checked on 13 September 2026. Other browser versions and Motion versions have not been tested.
import { useState } from "react";
import { motion } from "motion/react";
export default function App() {
const [visible, setVisible] = useState(true);
return (
<>
<button onClick={() => setVisible(!visible)}>Toggle</button>
<motion.div
initial={false}
animate={{ opacity: visible ? 1 : 0 }}
transition={{ duration: 0.2, ease: "linear" }}
>
Always mounted
</motion.div>
</>
);
}
- Verify that toggling at 100% works.
- Open DevTools, More tools, Animations, and set playback to 10%.
- Wait approximately 10 seconds, then toggle.
- The element waits before starting its approximately two-second fade. The wait depends on accumulated clock divergence.
Expected: the fade begins immediately and takes approximately two seconds.
Actual: the fade begins after a long delay. A manual observation was approximately 84 seconds. A separate instrumented run measured a negative animation current time that predicted approximately 128 seconds of remaining delay; that run was corrected before waiting for natural completion.
Root-cause evidence
In the installed motion-dom 13.2.0 implementation:
frameloop/sync-time.mjsobtains time from the frame timestamp orperformance.now().animation/AsyncMotionValueAnimation.mjschooses a creation/resolution timestamp asstartTime.animation/NativeAnimationExtended.mjsforwards the supplied start time.- The setter in
animation/NativeAnimation.mjsassigns it directly to the underlying WAAPI animation.
DevTools slows the document timeline while the JavaScript performance clock continues advancing at normal speed. The animation start time is therefore ahead of its timeline. This matches the Web Animations timing model, which calculates animation time from timeline time minus start time, multiplied by animation playback rate.
Approximately 100 ms after one toggle, the measurements were:
| Measurement (ms) | Motion | Native control |
|---|---|---|
| Document timeline | 105163.230 | 105163.230 |
| Animation start time | 117965.700 | 105153.190 |
| Animation current time | -12802.470 | 10.040 |
| Computed opacity, fading from 0 to 1 | 0 | 0.0502 |
The JavaScript performance clock read 118066 ms. Both animations reported individual playbackRate 1; DevTools slowed their shared document timeline.
At 10% timeline speed, a deficit of 12802.470 timeline milliseconds takes approximately 128 seconds of real time to recover.
Two interventions isolate the cause:
- Changing only the delayed Motion animation's
startTimetodocument.timeline.currentTimemade it advance immediately. After approximately 100 ms of real time, currentTime was 10.024 ms and opacity was 0.05012. It had completed at the sample approximately 2.3 seconds later. - Without Motion, assigning
performance.now()to a native animation'sstartTimereproduced the delay: currentTime was -32528.570 ms. An otherwise equivalent native animation assigneddocument.timeline.currentTimeadvanced to 11.704 ms over the same interval.
The comparison can be performed on two plain elements while DevTools is slowed:
const options = { duration: 200, easing: "linear", fill: "both" };
const frames = [{ opacity: 0 }, { opacity: 1 }];
const a = firstElement.animate(frames, options);
a.startTime = performance.now();
const b = secondElement.animate(frames, options);
b.startTime = document.timeline.currentTime;
Possible direction
The evidence points to using a timestamp outside the WAAPI timeline's clock domain when scheduling the native animation. Leaving native start-time scheduling to the browser or expressing the start time in the animation timeline's clock domain avoids the observed delay.
The runtime intervention is diagnostic, not a proposed production patch. A library fix needs to preserve Motion's synchronization, interruption, handoff, and custom-timeline behavior. These tests establish the mechanism in the tested configuration; they do not establish browser compatibility or whether DevTools should offer additional compatibility handling.
Measurement method
Chrome DevTools MCP evaluated scripts in the reproduction page. A wrapper around the native Animation.prototype.startTime setter recorded assigned values and both clock readings, then forwarded each value unchanged to the original setter. Samples read document.getAnimations(), animation timestamps, and computed opacity. The delay was also observed manually before instrumentation.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the CodeSandbox reproduction, then read frameloop/sync-time.mjs and the start-time handling in animation/AsyncMotionValueAnimation.mjs, animation/NativeAnimationExtended.mjs, and animation/NativeAnimation.mjs. Reproduce the delay with Chrome DevTools playback at 10% and compare Motion with the native control. Done means the fade starts immediately at slowed playback while preserving synchronization, interruption, handoff, and custom-timeline behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, performance, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100