Wrong size or blurry
The animation renders, but it is the wrong size, stretched, cropped, blurry, or sharp only after a resize.
Use this when
- The animation looks pixelated on Retina / 4K displays.
- The animation does not fill the slot you put it in.
- It gets sharper or larger only when you resize the browser window.
- It jumps in size on first load.
What you need
- The animation already loading (if not, see Animation will not load).
- The intended fit/aspect from
handoff.md.
The two golden rules
Rule 1 — Size with CSS, not with width/height attributes
<!-- WRONG -->
<canvas id="rive-canvas" width="500" height="500"></canvas>
<!-- RIGHT -->
<div style="width:100%; aspect-ratio: 16/9;">
<canvas
id="rive-canvas"
style="width:100%; height:100%; display:block; touch-action:none;"
></canvas>
</div>The width / height HTML attributes set the canvas's internal backing-store size and reset the WebGL context. Always size the canvas with CSS on the wrapper.
Rule 2 — Call resizeDrawingSurfaceToCanvas() on load and on resize
const r = new Rive({
src: "/animations/hero.riv",
canvas,
autoplay: true,
stateMachines: "State Machine 1",
onLoad: () => r.resizeDrawingSurfaceToCanvas(),
});
window.addEventListener("resize", () => r.resizeDrawingSurfaceToCanvas());This is what tells Rive to match the canvas backing store to the actual CSS size and the device pixel ratio (DPR). Without it, Retina screens render the animation at half-resolution — that is what "blurry" usually means.
If you use the React useRive hook, this is handled for you. If you use Framer, the prebuilt component does it. If you wrote your own code, you need both lines.
Fit and alignment
Match what handoff.md specifies. Common values:
import { Layout, Fit, Alignment } from "@rive-app/webgl2";
new Rive({
// ...
layout: new Layout({ fit: Fit.Contain, alignment: Alignment.Center }),
});| Fit | Behaviour | When to use |
|---|---|---|
Contain | Whole animation fits, may leave empty space around it | Default — preserves design |
Cover | Fills the canvas, crops the animation | Hero banners |
Fill | Stretches to fill, ignores aspect ratio | Almost never — distorts the design |
None | Renders at the artboard's native size | Pixel-exact UI elements |
Layout | Responsive layout engine (artboard adapts) | Only when handoff.md says so |
Aspect ratio mismatch
If your container is 16:9 but the artboard is 1:1, Contain leaves bars on the sides. Either match the container aspect to the artboard, or use Cover if cropping is acceptable.
Animation looks fine until I resize
Most often: resizeDrawingSurfaceToCanvas() is only called in onLoad. Add the window resize listener:
window.addEventListener("resize", () => r.resizeDrawingSurfaceToCanvas());If your container resizes for reasons other than window resize (responsive layout shifts, parent collapse/expand), call it then too:
const ro = new ResizeObserver(() => r.resizeDrawingSurfaceToCanvas());
ro.observe(canvas.parentElement);Do not let ResizeObserver write to canvas.width / canvas.height
Some examples online use a ResizeObserver callback that sets canvas.width = entry.contentRect.width and canvas.height = .... That breaks the renderer. Only call resizeDrawingSurfaceToCanvas() — Rive sets the backing store internally.
Mobile-specific
- Set
touch-action: noneon the canvas to stop the browser from interpreting drags as scroll/zoom. - Set
pointer-events: autoto ensure overlays do not block input. - Test on a real device — DPR on phones (often 2 or 3) shows blur issues that desktop hides.
Common mistakes
<canvas width="..." height="...">in HTML. Remove those attributes.- Manually setting
canvas.width = window.innerWidthin JS. Don't. Use CSS +resizeDrawingSurfaceToCanvas(). - No parent height. A canvas with
height: 100%inside a parent with no height is0pxtall. Give the parent anaspect-ratioor fixed height. - Forgetting the window resize listener. Renders fine at load, blurs after resize.
- Using
Fit.FillwhenContainwas intended — distorts the design.
If stuck, send this to Lupian
- A screenshot at the broken size and a screenshot of the intended look.
- Your CSS for the canvas wrapper.
- The
Fitand aspect ratio you used. - See Ask Lupian for help.

