When a Gasly order is complete, the customer lands on a receipt screen. The receipt was a static design: three stacked cards for the Gasly header, the order details and the payment breakdown, followed by Share and Download buttons.
I turned that screen into a small moment of delight. A charcoal receipt printer sits at the top of the screen and, as the screen opens, the Gasly receipt feeds out of its slot as one long strip of paper. When printing finishes, the Share and Download buttons rise in underneath. Tap the printer and it pulls the paper back in and prints it again.
Under the animation, the receipt is not a picture. Every value on it comes from the app, any row can be switched off, and the paper, the animation and the screen height all adapt to whatever the app sends.

Before opening Rive I wrote down the decisions that would shape the whole build, so the design, the animation and the engineering handoff all pulled in the same direction:
I designed everything at 428 × 828, the width of the original receipt frame, using Rive Layouts so that the width follows the phone and the height follows the content.

I kept the printer deliberately neutral so the receipt stays the hero. It matches a reference shot of a real receipt printer: a charcoal body with a deep navy-black slot, and no Gasly colour on it at all. The only orange on screen is the Total Paid amount.
At rest, the top of the receipt (logo and subtitle) stays tucked inside that shaded mouth, exactly as a real printed receipt hangs from the printer.

The obvious way to animate printing is to grow the paper or reveal it with a mask. Both look wrong: the content slides around inside the paper instead of travelling with it. So the receipt is never drawn line by line. It is one complete, fixed-size strip that starts parked above an invisible cut line at the centre of the slot, and the animation simply slides it straight down through that line.
Three pieces make that work:
Because the paper moves down, the bottom of the receipt comes out first: Total Paid appears before anything else, and the Gasly header is the last thing to leave the printer.

The print lasts 1.9 seconds, and I broke it into the beats of a real thermal printer:
I also added the sound of a receipt printer, synced to start with the feed at 200 ms and trimmed to the 1.45 s feed. The first version was too loud, so I brought it down by 10 dB. The app can mute it, and it never plays in Reduce Motion.

All of the behaviour lives in one Rive state machine, PrinterSM, with four layers running in parallel.
Nine pointer listeners drive it. Pressing a button darkens its fill and stroke and scales it to 0.97 in 90 ms; releasing eases it back in 140 ms and fires a trigger to the app. Pressing the printer dips it by 1 pt, and releasing it fires reprint.
Only the Printed state listens for reprint, so taps during printing or retracting are simply ignored and the animation can never be interrupted halfway. The press animations also start from whatever value is current, so a fast double tap never makes a button jump.


The receipt is driven by one view model, ReceiptVM, which is the contract between the Rive file and the app:
Every hidden row removes exactly 40 pt, from the paper and from the content height, so the app always knows how tall the receipt is. Because the feed is driven by a 0-to-1 progress value instead of fixed pixel keys, a shorter receipt still lands perfectly.
The Naira sign
One detail nearly broke the design. The receipt uses Geist, and Geist has no ₦ glyph: in Figma a fallback font had been quietly drawing it. Rive does not fall back, so a ₦ typed in Geist would render as an empty box. I made every ₦ its own text run set in Inter at the same size, weight and colour as the text around it, and the app sends plain numbers like "7,800". Rive adds the symbol, so the amounts read as "₦7,800" with no gap.
Long values and small screens
Labels never shrink; values are single-line, right-aligned and end in an ellipsis when they run out of room. Only horizontal insets stretch with the screen, so at 320 pt the row content is 240 pt wide, and every vertical measurement stays the same on every phone.

When the user has Reduce Motion turned on, the receipt does not slide or shake. It goes to a dedicated Appearing state: the paper is already fully out and simply fades in over 150 ms, then the buttons arrive as usual. There is no printer sound either.
My first plan was a 150 ms cross-fade straight into the Printed state. In testing, the blend mixed the two animations, so the paper visibly slid and the print state reported to the app went fractional for a moment. A purpose-built state fixed both: no movement at all, and the app gets a clean "printed" signal.

A few build decisions made the file robust enough to ship:

The printer shipped in the Gasly app as a single .riv file of about 1.7 MB, with fonts and sound included. It runs the whole receipt experience: printing, reprinting, share and download, live data, row hiding, responsive width and Reduce Motion, all from one artboard and one state machine.

