Back to lab

framebudget

A JavaScript library that measures how much of each 16.7 ms frame a device really has and turns on only the visual effects that fit. Slow phones stay smooth, fast phones keep everything.

product
framebudget

At 60 Hz every frame has a budget of 16.7 ms. Each visual effect on a page, parallax, page transitions, canvas animations, blur, sound, spends part of it. framebudget measures how much budget a device really has and allows only the effects that fit, so a slow phone never stutters and a fast one keeps everything.

Why I built it

The sites I make lean on motion: live hair strands on a salon’s home page, a gallery of screenshot styles with parallax bands. Most sites ship one set of effects to everyone, and the usual way out is a guess from core count, memory, or whether it is a phone. This site’s gallery guessed exactly like that, until framebudget took over. I wanted the device to be measured, not guessed.

How it works

  • Before the first paint. A small inline script in <head> runs a benchmark of about 2 ms and writes the decision on <html>, so an animation never starts only to freeze. CSS can gate an effect with no JavaScript at all: html[data-framebudget-effects~="parallax"].
  • A score, not a tier. The benchmark gives a score where 100 is the reference phone. Each effect has its own threshold and frame cost, with a margin so a device near the edge does not flip on every visit.
  • A second look. Once the page has loaded and the engine has warmed up, a longer benchmark refines the score and caches it for the next page.
  • A governor. It watches the real gaps between frames. When they stay late, it steps down the most expensive effect, or the very effect whose loop is stuttering.
  • Local learning. Effects that stuttered are remembered on the device and tried again after a few clean visits. Nothing leaves the device unless the site’s developer turns on anonymous sharing, which is off by default.

The whole decision is one object of thresholds and costs, so a site can register its own effects and tune the built-in ones. The reference numbers are still provisional, and they are being calibrated on real devices.

This page's decision

 

  • hover hover states
  • entrances signature and ribbon draw-in
  • shimmer light along the ribbon
  • pageTransition seam between pages
  • ribbonRedraw ribbon redraw
  • ribbonFull ribbon with every thread
  • carousel lab carousel
  • previewLoops animated previews
  • blur frosted notice and bar
  • parallax parallax gallery
  • sound interface sounds
  • canvasHiRes high resolution seam

On this site

This site runs framebudget on every page, with a few effects of its own registered next to the built-in ones. The signature and its ribbon draw themselves in under entrances, the light that keeps running along the ribbon is shimmer, and the seam that sews one page over the next is pageTransition, its canvas at full resolution only under canvasHiRes. The ribbon is drawn with every thread under ribbonFull and fewer below it; on the slowest devices it arrives as a still image. The seam, the carousel and the ribbon report their own frames to the governor, so a loop that stutters steps itself down. The screenshot styles gallery drops to its lite version without parallax. The Espaço Dora Santos site uses it too.

This site also shares framebudget’s anonymous measurements with framebudget.dev, which is how the reference numbers get calibrated on real devices. The privacy page lists what is sent and how to turn it off.

To see the decision for your device, open this page with ?framebudget: a panel shows the score, the state of each effect and buttons to force a tier. To see the site the way a slow phone does, force the Lite tier (and go back).

Using it

import { bootScript } from 'framebudget/boot';
import { budget } from 'framebudget';

// In <head>, before the CSS:
const head = `<script>${bootScript}</script>`;

// Anywhere after:
if (budget.allows('parallax')) startParallax();
budget.on('change', (snapshot) => setEffects(snapshot.effects));

The package is written in TypeScript and comes with React hooks. The site has the full tour, with a simulator that runs the page as a flagship, a mid-range phone or a budget phone from 2019.