#RequestAnimationFrame
I'm guessing there's a few APIs that you would have to be very careful/break. requestAnimationFrame, the web animations API, css transition events?

I guess if you can know that there's no event listeners etc and then run in the "fast" path that could be interesting.
November 5, 2026 at 9:16 AM
Noticed @capacities.bsky.social has rather smooth Mermaid integration, so used it to try and untangle my game’s architecture a bit. Handy! Not a huge fan of Mermaid’s syntax or default styling though.
November 6, 2026 at 10:31 AM
🌟 Wipr 2.44 is out now! 🌟

I've completed the Global Privacy Control
implementation! Read more here:

Also, Extra can now stop pages from doing nasty stuff by hijacking yet another browser bit called "requestAnimationFrame".

kaylees.site/wipr2.html
Wipr 2.43
kaylees.site
October 29, 2026 at 3:58 PM
We now have @duncanpetrie.com filling the slot next Tuesday:
Bringing Spiders to the Web with RequestAnimationFrame
londonwebstandards.org/talks/bringi...
Bringing Spiders to the Web with RequestAnimationFrame | London Web Standards
Animating on the web has never been easier. But what if you want to make life difficult for yourself? In this introduction to requestAnimationFrame, we’ll throw away the conveniences of modern life an...
londonwebstandards.org
October 29, 2026 at 4:03 PM
Vue nextTick waits for a DOM update. requestAnimationFrame runs before repaint. Match the wait to the missing dependency.
threadeast.xyz/guides/china...
China Developer Brief: nextTick Is a DOM Boundary, Not a Paint Receipt
A Juejin Vue explainer checked against official API and browser documentation: distinguish the DOM flush, repaint scheduling and usable chart dimensions.
threadeast.xyz
November 2, 2026 at 4:06 AM
Web Compatibility Support • Re: aueb.gr - text layout/scrolling issues
Thanks for the marquee info. And thanks to sinfulosd for massively improving Greasemonkey for UXP. Took me an insane amount of time (don't ask), but got AI to fix the layout and restore scrolling with a userscript. The result is satisfying but I don't know if it was worth the time. If I got the whole thing correctly, the only drawback was not being able to use the "proper" method (CSS Animations). It also wasn't able to identify why it failed to work and move the text. requestAnimationFrame(). was used instead. Can't say how "heavier" or inappropriate this is. Τhe fully commented userscript: // ==UserScript== // @name AUEB Marquee Compatibility // @namespace aueb-marquee-compatibility // @version 0.5 // @description Restore scrolling news ticker on the AUEB website // @author ChatGPT (GPT-6) // @include https://www.aueb.gr/* // @run-at document-end // @grant none // ==/UserScript== (function () { "use strict"; /* * AUEB Marquee Compatibility * * Site-specific compatibility workaround for the scrolling news * ticker on the Athens University of Economics and Business website. * This is NOT a general-purpose HTML <marquee> polyfill. * * The script addresses three separate requirements: * * 1. Layout: Keep the news ticker within its intended area instead * of allowing its contents to overflow the surrounding page. * * 2. Scrolling: Reproduce the marquee's horizontal movement using * requestAnimationFrame(), with hover/focus pausing and automatic * resumption. * * 3. Content width: Ensure that the movable container spans the * entire news sequence, preventing premature text clipping. * * The original news content, links, and their order are preserved. * Animation is suspended in background tabs. * * No external libraries or network requests are required. */ // Locate the ticker and prevent duplicate initialization. var marquee = document.querySelector("marquee"); if (!marquee || marquee.hasAttribute("data-aueb-fixed")) return; marquee.setAttribute("data-aueb-fixed", "true"); /* * Group the original news items into one movable container. * Existing DOM nodes are moved rather than copied or recreated, * preserving their links, content, and order. */ var content = document.createElement("span"); content.className = "aueb-ticker-content"; while (marquee.firstChild) content.appendChild(marquee.firstChild); marquee.appendChild(content); /* * Remove inline handlers intended for the native marquee. * The replacement animation manages pausing independently. */ ["onmouseover", "onmouseout", "onfocus", "onblur"].forEach(function (attr) { marquee.removeAttribute(attr); }); /* * Establish the ticker's visible area. * * The outer element occupies the available width, keeps the news * on one line, and clips content outside its boundaries. * This corrects the original layout overflow independently * of whether scrolling is active. */ marquee.style.setProperty("display", "block", "important"); marquee.style.setProperty("width", "100%", "important"); marquee.style.setProperty("max-width", "100%", "important"); marquee.style.setProperty("min-width", "0", "important"); marquee.style.setProperty("overflow", "hidden", "important"); marquee.style.setProperty("white-space", "nowrap", "important"); /* * Prepare the inner container for horizontal movement. * * During testing, CSS keyframe animations (using 'transform' * or 'left') did not produce visible scrolling, even though * the animation was reported as running and computed transform * values changed. * * Direct JavaScript updates to 'left' worked correctly, so * this implementation uses requestAnimationFrame() for smooth, * rendering-synchronized movement. * * The underlying cause of the CSS animation behavior was not * conclusively identified; it may involve the site's layout * or its interaction with the browser's rendering engine. */ content.style.setProperty("display", "inline-block", "important"); content.style.setProperty("position", "relative", "important"); content.style.setProperty("white-space", "nowrap", "important"); content.style.setProperty("animation", "none", "important"); content.style.setProperty("transform", "none", "important"); /* * Ensure the movable container spans the entire news sequence. * * Its automatically calculated width may be limited to the * visible ticker width, even though its children extend much * farther horizontally. Moving such a narrow container can * cause the overflowing text to be clipped prematurely. * * scrollWidth measures the full content extent. Assigning it * explicitly ensures that all news items remain available * throughout the animation. */ content.style.setProperty("width", content.scrollWidth + "px", "important"); /* * Animation state. * * Speed is expressed in pixels per second, independent of the * display refresh rate. Starting at zero makes the first * news item visible immediately. * * requestAnimationFrame() was selected instead of a fixed * setInterval() timer because it synchronizes position updates * with the browser's rendering cycle, producing smoother movement. * * Direct 'left' updates may involve layout/paint work, so * animation is suspended when the document is hidden. */ var speed = 50; var x = 0; var previous = null; var frameId = null; function frame(time) { frameId = null; // No animation updates are needed in background tabs. if (document.hidden) { previous = null; return; } /* * Pause during mouse hover or keyboard focus. * * Checking the current state on every frame allows the * animation to resume without depending on mouseleave * or other event notifications. */ var paused = marquee.matches(":hover") || marquee.contains(document.activeElement); if (!paused && previous !== null) { /* * Calculate movement using elapsed time. * Cap individual steps at 10 pixels to avoid sudden * jumps after rendering delays. */ x -= Math.min((time - previous) / 1000 * speed, 10); /* * Restart at the right edge after the complete * news sequence has moved out of view. */ if (x < -content.scrollWidth) x = marquee.clientWidth; content.style.setProperty("left", x + "px", "important"); } // Refresh the time reference even while paused. previous = time; frameId = requestAnimationFrame(frame); } /* * Stop and restart frame scheduling when tab visibility changes. * Resetting the time reference prevents a positional jump * when returning to the page. */ function stop() { if (frameId !== null) cancelAnimationFrame(frameId); frameId = null; previous = null; } function start() { if (frameId === null && !document.hidden) { previous = null; frameId = requestAnimationFrame(frame); } } document.addEventListener("visibilitychange", function () { if (document.hidden) stop(); else start(); }); // Display the first news item immediately and start scrolling. content.style.setProperty("left", "0px", "important"); start(); })(); * * *
forum.palemoon.org
October 30, 2026 at 2:07 AM
Break this down for me, I don’t quite understand what you’re describing.

Though admittedly, I’ve hammered handle.update() in requestAnimationFrame to power a threejs scene and Remix didn’t even break a sweat, so I kinda get the idea.
October 22, 2026 at 8:30 PM
requestAnimationFrame Explained (With Examples)

How requestAnimationFrame works in JavaScript: the animation loop, delta time for smooth motion on any screen, cancelAnimationFrame, throttling scroll handlers, and when to use CSS instead.
requestAnimationFrame Explained (With Examples)
How requestAnimationFrame works in JavaScript: the animation loop, delta time for smooth motion on any screen, cancelAnimationFrame, throttling scroll handlers, and when to use CSS instead.
markodenic.com
October 12, 2026 at 9:01 AM
We had Duncan Petrie talking about some incredible and creative ways of using requestAnimationFrame which involved Fabergé eggs and a springy chicken crossing the road and very well animated spiders! @webdevconf.com
October 9, 2026 at 10:12 AM
(Adam Silver meme) get ready to [requestAnimationFrame()] buddy
October 6, 2026 at 9:47 PM
Ha, blending `prefers-reduced-motion` with an RG35XX is a first.

I haven't tried throttling `requestAnimationFrame` yet, but now I'm curious.
September 22, 2026 at 6:03 AM
Interesting combo! On the RG35XX, have you measured the frame‑rate drop when you cap rAF to 30 fps? I found that using `requestAnimationFrame` with a manual timestamp check respects `prefers-reduced-motion` and keeps battery life decent. #webdev
September 22, 2026 at 6:13 AM
Interesante lo de los handhelds viejos; con el RG35XX puedes combinar `prefers-reduced-motion` y limitar el `frameRate` en CSS (`animation-play-state: paused`) para ver la diferencia sin forzar la GPU. ¿Has probado con `requestAnimationFrame` throttling? #devtips
September 22, 2026 at 12:28 AM
Actualmente, quando tenho que rever um conceito core ou fundamental de programação, tenho tendência a ver apenas vídeos ou textos produzidos antes de 2022. Tem o seu perigo (embora os fundamentos sobrevivam à passagem do tempo). Mas começo a não ter confiança em vídeos e textos recentes.
Jake Archibald on the web browser event loop, setTimeout, micro tasks, requestAnimationFrame, ...
YouTube video by JSConf
www.youtube.com
September 21, 2026 at 3:16 AM


Checklist:
- Animate transform and opacity only, in anything that runs per frame.
- Prefer CSS or the Web Animations API so animations can live on the compositor.
- Route JavaScript-driven updates through requestAnimationFrame: reads first, writes second, never interleaved.
September 21, 2026 at 5:32 AM
Just had my twice-yearly rewatch of @jakearchibald.com great talk on the event loop. I have to ask: are there any updates on the soda water conspiracy?

youtu.be/cCOL7MC4Pl0?...
Jake Archibald on the web browser event loop, setTimeout, micro tasks, requestAnimationFrame, ...
YouTube video by JSConf
youtu.be
September 9, 2026 at 3:59 PM
A UI column in Slaide's editor was stuck open for one user this week - a background tab, right panel wedged at 340px. requestAnimationFrame does not fire in a hidden tab, so my sync callback queued and never ran. Switched it to setTimeout.

I should not still fall for this one.

#BackgroundTabs
August 27, 2026 at 1:20 PM
このゲームには、これまで作ってきたゲームにはなかった特徴がある。動いているものが1つもないということである。ブロック崩しのボールも、落ちものブロックの落下も存在しない。プレイヤーがカードをクリックした瞬間だけ画面が変わり、あとは止まっている。1秒間に何十回も画面を描き直す必要がないため、「4.3 ブロック崩し」で使った requestAnimationFrame によるゲームループも、「4.4 落ちものブロック」で使ったデルタタイムの計算も不要である。
August 23, 2026 at 12:28 AM
requestAnimationFrame
August 19, 2026 at 7:55 AM
What the browser can actually tell you about your hardware (and what it can't)
I spent a while building browser-based hardware diagnostics and came away with a much clearer sense of where the web platform is genuinely capable and where it quietly lies to you. Notes below, with live demos for each API so you can poke at them yourself. ## Refresh rate: `requestAnimationFrame` is the only signal you get There's no `screen.refreshRate`. The only approach is timing `requestAnimationFrame` callbacks and inferring the rate from the median frame delta: const deltas = []; let last = performance.now(); function tick(now) { deltas.push(now - last); last = now; if (deltas.length < 180) requestAnimationFrame(tick); else { const sorted = deltas.slice().sort((a, b) => a - b); console.log(Math.round(1000 / sorted[sorted.length >> 1])); } } requestAnimationFrame(tick); Two gotchas that cost me time. Use the **median** , not the mean — a single dropped frame wrecks an average. And browsers throttle `rAF` in background tabs, so the measurement is meaningless unless the tab is visible; gate it on `document.visibilityState`. (live version) ## Screen dimensions: four different answers, all "correct" `screen.width`, `window.innerWidth`, `window.devicePixelRatio` and `screen.availWidth` measure genuinely different things, and the one people usually want — actual native panel resolution — is `screen.width * devicePixelRatio`. Except that's still CSS-pixel derived, so on a scaled display it can disagree with what the panel physically is. The browser simply does not expose true hardware resolution. (demo) ## Keyboard: `event.code` vs `event.key`, and the keys you never receive `event.key` is layout-dependent, `event.code` is physical position — for a hardware tester you want `code`. The real limitation is that some keys never reach JS at all: `PrintScreen` often doesn't fire `keydown`, `Meta` combinations get swallowed by the OS, and `Fn` isn't a browser-visible key on most laptops. N-key rollover testing works surprisingly well though, since you just track the size of the currently-held `Set`. Cheap keyboards drop inputs at 3-4 simultaneous keys and it's very visible. (demo) ## Pointer events beat mouse events, with one catch `pointerdown`/`pointerup` unify mouse, touch and pen, and `pointerId` lets you track multi-touch properly. For detecting the classic worn-switch double-click fault, you just measure the gap between consecutive `pointerdown` events on the same button — under ~80ms with no intervening movement is almost certainly a hardware fault rather than a human. (mouse demo, touch demo) The catch: `preventDefault()` on `pointerdown` kills the subsequent compatibility mouse events, which will silently break anything expecting them. ## Gamepad API: polling only, and it starts asleep No events for axis movement — you have to poll `navigator.getGamepads()` inside your rAF loop. Worse, controllers don't appear at all until the user presses a button, for fingerprinting reasons. That confused me for an hour: the array is full of `null` until first input. Stick drift shows up as a resting axis value that isn't 0. Anything past about 0.08 at rest is a worn potentiometer. (demo) ## Media devices: labels are gated behind permission `navigator.mediaDevices.enumerateDevices()` will happily list devices before you request permission — but every `label` is an empty string. You only get human-readable device names after `getUserMedia()` succeeds. So a device picker UI has to either request permission first or show useless blank entries. For mic level metering, `AnalyserNode` with `getByteTimeDomainData` and computing RMS is more stable than frequency data. (mic demo, webcam demo, speaker demo) ## Dead pixels: nothing programmatic, and that's fine There is no API. The only viable approach is filling the viewport with solid colours via the Fullscreen API and letting the human look. Worth remembering that not every problem needs a programmatic answer — sometimes rendering `#ff0000` across the whole screen _is_ the tool. (demo) ## The pattern underneath all of this Every hardware-adjacent API on the web has been deliberately blunted for fingerprinting resistance. You get relative measurements, permission-gated labels, and input-gated device lists. That's the right trade, but it means "detect the user's hardware" is mostly not a solvable problem — and the honest tools are the ones that measure behaviour rather than claim to read specs. All ten of these run client-side with nothing uploaded if you want to compare notes: the full set is here. Happy to hear if anyone has found better approaches, particularly on refresh rate — the rAF median approach still feels like a hack.
dev.to
August 16, 2026 at 6:51 AM
For anyone wrestling with VoiceOver cursor issues in Safari when focusing elements that have just been added to the DOM, you'll want to wait for painting to be complete with something like

requestAnimationFrame(
() => requestAnimationFrame(
() => element.focus()
)
) […]
Original post on tech.lgbt
tech.lgbt
August 14, 2026 at 4:12 PM
So if you’re currently doing a requestAnimationFrame loop and updating the SVG properties on every frame, React *can* slow things down by a non-negligible amount, though React’s diffing is usually quite a bit faster than the actual DOM update, so it’s a comparatively small cost.
October 14, 2025 at 8:15 PM
Hm so I haven’t tried to *animate* text along a path, but it should be possible with <textPath> and the startOffset property. No idea if it can be animated with CSS transitions, but if not, you could use a requestAnimationFrame loop, or a library like Motion!
August 18, 2025 at 4:51 PM
slowmo.dev by @seflless.bsky.social is pretty damn cool — "Slow down, pause, or speed up time of any web content."

Here's how it works.
February 25, 2026 at 5:19 AM