A Rive file can break without telling you
A .riv file is a compiled program, not an image. It can load fine and still ignore a click, pop at the loop or feel dead, so I built a linter and a method to catch that before it ships.
A .riv file looks like an image that moves. It's actually a compiled program, and programs don't always complain when they're wrong.
It can compile, load and look fine on the first try while carrying an input nobody reads, so your app sends values into the void. Or a state you can enter but never leave. Or a transition that snaps to a pose instead of arriving at it. Or a loop that pops right at the seam because the last frame doesn't match the first. Or a rotation written in degrees when Rive wants radians, and your icon spins fifty-seven times.
None of that throws an error. So I built something that speaks up on the file's behalf.
Catching what stays quiet
rive-animator is my toolkit for writing Rive from code, made for AI coding agents and for developers. It starts with a linter that checks the scene before it gets near production, and it reads the compiled .riv too. RML011 flags an input that's declared but never read by any condition or listener. RML012 flags a transition into a pose with no blend, the one that snaps. RML013 flags a loop whose last key doesn't match its first. A second set checks the binary: truncated files, no artboard, no layers, duplicate input names.
What the linter can't catch, I render. A state with no way back only shows up when someone clicks twice, so every recipe is recorded in the real Rive runtime, with a script moving the inputs the way an app would. There are nine recipes, from a toggle and a like heart to a progress ring and a mascot.
A mascot that breathes
The other half of the problem is the file that works perfectly and still feels dead. Everything moves at the same time, every pose stops cold, colors swap instead of blending, and between one action and the next nothing happens.
The mascot is a round coral character with a sprout. Leave it alone and it breathes. Now and then it blinks. When the cursor shows up, its eyes follow. Click, and it crouches, hops and lands happy. When the cursor leaves, it watches it go.
The hop is the action, but what makes it feel like someone is home is what it does when nothing is happening: breathing, blinking, looking around. My rule is one main action plus idle layers that never stop. The method comes from rossark0's motion skill, which I translated to RML.
A technical detail I liked solving: Rive eases every keyframe segment on its own, so a chain of poses brakes at each one. A script of mine, motion_keys.py, writes the curves so the motion passes through every pose without stopping or overshooting.
A file that loads is not a file that works. I don't call a .riv good anymore until it has passed the linter and I've watched it move. The repo is open source under the MIT license.