惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Engineering at Meta
Engineering at Meta
博客园 - 三生石上(FineUI控件)
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Schneier on Security
Vercel News
Vercel News
A
Arctic Wolf
G
Google Developers Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网
AWS News Blog
AWS News Blog
V
Visual Studio Blog
Cyberwarzone
Cyberwarzone
爱范儿
爱范儿
博客园 - 司徒正美
NISL@THU
NISL@THU
N
News | PayPal Newsroom
N
News and Events Feed by Topic
Scott Helme
Scott Helme
I
InfoQ
Project Zero
Project Zero
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Tenable Blog
Help Net Security
Help Net Security
D
Docker
IT之家
IT之家
人人都是产品经理
人人都是产品经理
C
Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
PCI Perspectives
PCI Perspectives
aimingoo的专栏
aimingoo的专栏
博客园 - Franky
G
GRAHAM CLULEY
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
博客园 - 【当耐特】
L
LINUX DO - 热门话题
J
Java Code Geeks
C
Cyber Attacks, Cyber Crime and Cyber Security
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
The Hacker News
The Hacker News
H
Hacker News: Front Page
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
SecWiki News
SecWiki News
T
Tailwind CSS Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
罗磊的独立博客

Lobsters

Lunacy | Red Vice CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless That one time I used Go panics for flow control A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
CSS vs. JavaScript • Josh W. Comeau
Josh W. Comeau · 2026-05-28 · via Lobsters
Introduction

One of the most common questions around animation performance is whether JS-based animations are slower than CSS-based ones. Should we always strive to use CSS transitions, or is it OK to use JavaScript animation libraries?

There’s a surprising amount of nuance to this question, and I think that the conventional wisdom isn’t quite right. In this post, we’re going to dig into this question and see the differences for ourselves!

Link to this headingComparing CSS keyframes to JavaScript loops

Let’s suppose we’re building the following animation:

We can wire this up with a CSS keyframe, like this:

@keyframes bounce {
  to {
    transform: translateX(calc(var(--bounce-magnitude) * -1));
  }
}

.ball {
  --bounce-magnitude: 200px;
  animation: bounce 1000ms infinite alternate;
}

(I’m using a CSS transform for this animation because it produces the smoothest motion. In cases where the container size is dynamic, we would need to calculate and apply --bounce-magnitude in JS.)

Alternatively, we could implement this animation using JavaScript! Before we consider JS libraries like GSAP or Motion, let’s start with a plain JS version:

const startTime = performance.now();

const ball = document.querySelector('.ball');

function animate() {
  const elapsedTime = performance.now() - startTime;

  // ✂️ Calculate `x` based on the amount of time that has passed.

  ball.style.transform = `translateX(${x}px)`;

  window.requestAnimationFrame(animate);
}

This code uses requestAnimationFrame to run the animate function on every frame (60 times per second on most displays). I’ve cut out the main logic to calculate x since it’s a bit complicated and not relevant for the topic at hand, but you can see the full code(opens in new tab) if you’re curious.

Here’s the question: which approach do you think runs more smoothly?

I think for most of us, our intuitions would tell us that the CSS version is more performant. And our intuition is correct, but maybe not for the reasons we think. 😅

You might think that the JS version is slower because it has to do all that extra work calculating the x value on every frame, or that there’s an extra cost to “crossing the bridge” between JavaScript and the DOM. But modern browser engines can tackle all of that stuff without breaking a sweat; even on low-end devices, that work happens in a tiny fraction of a millisecond, way too quick to affect the framerate of our animation.

But there’s one significant difference: the JavaScript version runs on the main thread, along with everything else happening in our application. CSS transitions and keyframe animations run on a separate thread, so they aren’t disrupted when stuff happens in JavaScript.

I’ve taken the liberty of creating a simulation. Click the “Play” button to run the demo. Every few seconds, the main thread will be blocked. Notice what effect it has on these two animations:

In modern web applications, the main thread does a lot of work. JavaScript frameworks like React are constantly making updates to the DOM, to keep it in sync with application state. Every time we make a fetch request (eg. to load more data, or to refresh existing data), that response has to be parsed by the main thread.

So, if you’ve ever seen a spinner freeze for a moment before the UI is updated, this is why! JavaScript-based animations have to compete for processing power with the rest of the application.

Link to this headingComparing animation libraries

In the example above, I used a requestAnimationFrame loop to update the UI on every frame in JavaScript. This is a pretty low-level technique; in practice, many developers use JavaScript libraries that provide a higher-level abstraction.

Let’s compare two popular animation libraries, Motion(opens in new tab) (formerly Framer Motion) and GSAP(opens in new tab):

Huh! Both Motion and GSAP are JavaScript-based, so you might expect them to share the same limitations of running on the main thread. But somehow, Motion manages to keep the animation running smoothly even when the main thread is occupied. 🤔

The secret is that Motion uses the Web Animations API(opens in new tab) (WAAPI) under the hood. WAAPI is essentially a JavaScript interface that hooks into the same low-level animation engine as CSS keyframe animations. So, Motion is able to run its animations on a separate thread, avoiding the main pitfall of most other JavaScript animation libraries! 😮

To be fair to GSAP, it’s an enormously powerful library which includes features that probably aren’t compatible with WAAPI. So, it’s not that GSAP made the wrong choice, it’s that they’re choosing different trade-offs.

In my own work, I try to use native CSS animations/transitions whenever I can. When I run into situations that CSS alone can’t handle, I try to use a library like Motion, which solves the problem without the drawbacks typically associated with JS libraries.

That said, CSS has become so powerful that there really aren’t that many cases where we need to reach for an animation library these days; new APIs like View Transitions, linear(), and Animation Timeline make it possible to do all sorts of stuff without JavaScript. ✨

We explore all of these tools, and so much more, in my brand-new course, Whimsical Animations(opens in new tab). I’ll show you how I design and implement top-tier animations using modern CSS, JavaScript, SVG, and Canvas.

Whimsical Animations, a course from Josh W. Comeau

These days, LLMs are great at generating syntax, but we still need to use our own judgment. In this blog post, we learned about the performance considerations between CSS and JavaScript, and my course is full of stuff like this. So, regardless of whether you’re writing code by hand or not, this course will help you create stunning animations and interactions.

You can learn more about it here:

Last updated on

June 3rd, 2026

# of hits