Lee Hardy Dydo

Making the Kiosk Smooth

October 18, 2026 · Chromebook Kiosk, part 2 of 2

The kiosk screen: the words IT BOOTS in cream letters with a colored glow, the name kiosk-q0m7 underneath, a firework bursting above and rocket trails rising from the bottom, on a dark navy background
Where it ended up: the same fireworks, drawn with WebGL, at 60 frames a second on the Chromebook.

1Choppy

Part 1 ended with the Chromebook booting straight into a success screen: a glowing title, fireworks, a little screen shake when a shell bursts. It worked, but it stuttered. Watching it, you could count the frames.

The first look at the machine told most of the story. One of Firefox's processes was using about a full CPU core, and the graphics chip, a Mali-G52 with a working open-source driver, was sitting near its lowest clock, 366 MHz out of a possible 1 GHz. The drawing was happening on the CPU, one core at a time.

2Turning the GPU on wasn't enough

Firefox keeps GPU drawing off for graphics drivers it hasn't tested, and this chip's driver is one of them. Six settings in the kiosk's Firefox profile force it on: hardware WebRender, and GPU drawing for the 2D canvas the fireworks use. After a restart, Firefox had the graphics device open and the GPU climbed to its top clock.

It was still choppy. The animation used the canvas's 2D drawing commands: every spark, often a couple of hundred at once, was its own circle, and every letter of the title got a fresh blur for its glow on every frame. Moving that onto the GPU one command at a time didn't remove the cost of issuing all those commands from a single thread.

3Leaving the browser

The next question was whether a browser was the right tool at all. The kiosk's Linux had three alternatives in its package list: LÖVE, a small 2D game engine programmed in Lua; raylib, a C library for games; and mpv, a video player that could loop a pre-rendered clip. I went with LÖVE, which needs no compiler and swaps in for Firefox under the same display server.

The port is under 300 lines of Lua. The difference is in how it draws: all the sparks go to the GPU as one batch, added together where they overlap, and each letter's glow is blurred once at startup and reused. It ran at a steady 60 frames a second, the screen's limit, using about 3% of the CPU.

Two things went wrong on the way. The first version drew at 800 by 600 and got stretched, because the display server hands every new window that size and only resizes it to the full screen a moment later; it now rebuilds itself whenever the window changes size. And it looked flatter than the browser version. The difference was in how the original drew: everything, title included, went into one canvas that fades a little each frame instead of clearing. The title's glow builds up over several frames, and the letters leave a soft smear when they bob. Because Firefox was running slowly, the fade also ran fewer times a second, so the trails lasted longer. Matching that in LÖVE brought the look back.

4Back to the browser, with WebGL

There was a middle path: stay in Firefox, but draw with WebGL instead of the 2D canvas. It's the same idea as the LÖVE version. The page keeps one buffer that fades each frame, sends all the sparks and rocket trails to the GPU as one list of triangles, and renders each letter and its glow into a texture once, using the canvas's own blur so the glow looks the same as before. If a browser has no WebGL, the page falls back to the 2D version.

It also runs at 60 frames a second, with Firefox's busiest process down to about 2% of the CPU. Pressing F on the kiosk's keyboard shows the frame rate and which renderer is running.

Three screenshots of the kiosk side by side. Left: the original Firefox version, with a gray haze over the background and rocket trails. Middle: the LÖVE version, with a plainer cream title and fireworks. Right: the WebGL version, with colored glows on the title and a frame-rate readout reading 60 fps in the corner
Left to right: the original 2D canvas in Firefox, the LÖVE port, and WebGL in Firefox. The gray in the left frame is the flash after a burst.

One thing looked broken and wasn't: after the switch, the kiosk kept showing the old version for several minutes. The kiosk's small web server sends no caching instructions, so Firefox applied its default freshness rule, which kept serving the saved copy. Everything on the kiosk is local, so its Firefox profile now has caching turned off.

Frames a secondCPUGPU clock
Firefox, 2D canvaschoppyabout one core366 MHz
Firefox, 2D, GPU forced onchoppyabout one core1 GHz
LÖVE60about 3%near 1 GHz
Firefox, WebGL60about 2%near 1 GHz

CPU percentages are shares of all eight cores, read with top on the kiosk. I didn't measure the choppy versions' frame rates; the page had no counter yet.

5Why the web version stays

LÖVE has its advantages: a fraction of Firefox's memory, a faster start, and nothing for a browser update to break. But with WebGL closing the speed gap, the web gives this project more:

The LÖVE version stays in the project anyway. One command switches the kiosk between the two, which is useful for anything that's pure animation.

6Next