05 / 08

Performance grades

The field and lab MotionScore grades, and gating merges with motionscore Guard.

Performance grades

Pages carry two MotionScore grades. The lab grade comes from a real browser audit of the public page (the same audit score.motion.dev runs; the badge links to the full report; grades can be up to a week old). The field grade comes from your real visitors: the tracker passively observes Largest Contentful Paint, an Interaction to Next Paint approximation, Cumulative Layout Shift and long animation frames, and sends one aggregate beacon per view. It also reads, once per view, whether the visitor has asked for reduced motion: reported per page as a share, never folded into the score (preferring reduced motion is not a fault), so you can see how much of your audience your animation work actually reaches. No new identifiers, no consent implications.

Field scoring (v1 methodology, subject to calibration): per page and device class we take sampling-weighted p75s, score each metric 0-100 by linear interpolation from the web-vitals "good" threshold (full marks) to a failure point calibrated so the official "poor" boundary scores 50, and combine them weighted for Motion's priorities: long animation frames 35%, interaction latency 30%, layout shift 20%, loading 15%. Weights renormalise when a browser cannot measure a metric (Safari and Firefox contribute loading metrics only and never drag a grade). Desktop and mobile are scored separately and averaged, exactly as MotionScore averages viewports. A device class needs 50 weighted views in range before it grades; loading and interaction metrics cover initial page loads, not in-app route changes.

MotionScore Guard (CI)

The lab grade is also a gate you can put in front of a merge. The motionscore CLI runs the same audit and exits non-zero when a page's overall grade falls below the tier you set, so a pull request that regresses animation performance fails its check.

npx motionscore https://preview.myapp.dev --threshold A

The audit runs on your own CI runner, so local Guard runs are free and unlimited on any paid plan. Drop it into a workflow to gate every pull request:

# .github/workflows/animation-budget.yml
name: animation-budget
on: pull_request

jobs:
  guard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: MotionScore Guard
        run: npx motionscore "$PREVIEW_URL" --threshold A --token "$MOTIONSCORE_TOKEN"
        env:
          PREVIEW_URL: https://preview.myapp.dev
          MOTIONSCORE_TOKEN: ${{ secrets.MOTIONSCORE_TOKEN }}

Guard is included with Pro and Scale. Tier order, worst to best, is F, D, C, B, A, S, so --threshold A requires an A or an S. A --cloud run audits on our browsers instead of yours and draws from your monthly on-demand audit pool; local runs never do.