How we built Farm Pop: a slot game where every symbol is a living 3D character

· 10 min read · Raw Fun Gaming

Farm Pop key art: the farmer waving among the farm animals in front of a red barn

Farm Pop is a game we built at Raw Fun Gaming: a 7×7 cluster-pays slot where the “symbols” are not spinning icons — they are fully animated 3D pets. A dog, a cat, a chick, a rabbit, each one idling, running as the reels move, jumping when they win, and burning away in a dissolve when a cluster pops. Match five or more touching symbols, they dissolve, everything above tumbles down, and the cascade continues. Trigger free spins and the whole farm sinks into night — lanterns on, fireflies out — until the bonus ends.

It runs entirely in the browser, built for Stake, where most players are on mobile Safari. That combination — a boardful of skinned, animated characters, on WebGL, on the worst-case browser — is where all the interesting engineering lives.

The client is a choreographer, not a game

Like every game we ship, the outcome is decided before the first frame renders. A math engine pre-generates the full result set — “books” of simulated rounds, one file for the base game and one for the bonus — and the web layer feeds Unity a script of typed events for the round it drew: reveal (here is the board, with per-column anticipation flags), win (these clusters, these amounts), tumble (remove these, drop in those), grid boosts, free spins.

Unity’s job is to perform each event beautifully and then say “done.” That handshake is the entire contract: every event ends with a completion message back to the web layer, which only then sends the next one. Nothing advances on a timer; nothing races. It also makes the system forward-compatible in a way we like: if the web sends an event type the engine does not handle yet, the interface auto-completes it back immediately instead of deadlocking the round. A new mechanic can ship in the math and the webfront before the Unity presentation exists, and the game degrades gracefully instead of hanging.

The deeper win is what this does to animation sequencing — the thing slot games live or die on. Because each event is a discrete, awaited performance, the tumble choreography (dissolve → despawn → compact → spawn → settle) can be genuinely asynchronous inside while still being strictly ordered outside. Column-by-column settles, staggered win animations, anticipation delays — all free to overlap internally, all guaranteed to finish before the next event starts. Even anticipation is authored by the math: the reveal event flags which columns should tease, and turbo mode’s settle-everything-at-once fast path is explicitly disabled when a flag is present. You never skip the tease.

A side effect we use constantly: a debug controller can load a book straight from disk in the editor and replay it against the same handshake, so the entire game is testable — every mechanic, every cascade — without a webfront running at all.

A state machine wearing a fur coat

The heart of the engine is a small state machine on every symbol: Idle, Spinning, Settling, Landed, Anticipating, Win, Dissolving, Clicked. The engine drives states; a game-specific subclass translates them into whatever the art can do. Farm Pop’s translation layer maps states onto the pet animators: idle loops (picked at random so a row of seven cats does not breathe in unison), a run cycle while scrolling and falling, a jump for wins, a landing animation that plays only on a symbol’s first settle — tumbles re-settle existing symbols and should not retrigger it.

Because symbols are characters, they can do character things. Tap one and it plays a reaction animation with its own sound. The scatter — a little farmer — spawns with randomized outfits, hair, and a 50/50 hoe-or-pitchfork prop, so no two look alike; during anticipation, when one more scatter would trigger the bonus, he digs. That is the kind of storytelling a flat symbol atlas cannot do, and it costs nothing extra: it is the same anticipation state every game built on this engine gets.

The price of living symbols is that everything must survive pooling. Forty-nine skinned characters churn through every tumble, so all of them come from object pools — and Unity’s Animator makes pooled characters expensive by default, because re-enabling one triggers a full avatar rebind. A one-line setting (keepAnimatorStateOnDisable) skips that. The pooling discipline runs deep: when the backend awards position multipliers, the winning symbol visually duplicates into a vertical stack of clones, and those clones are pooled too, with their animators rebound on dequeue and their materials restored on return. When a symbol dissolves, its clones share the same dissolve material instance, so the whole stack burns away in perfect sync without any coordination code.

That multiply stack is our favorite piece of pure juice in the game. It is not a loop — it is a recursive wave: the original squashes and pops, and at the peak of its overshoot the first clone starts rising out of it; when that clone arrives, it pops and launches the next one, like a stadium wave traveling up the stack. The glow flash is derived from the same motion — rim-light intensity is computed from how far the scale overshoots 1.0 — so one animation curve drives both the bounce and the flash, through a shared material property block, with zero extra material instances. And because a tall stack clips the top of the frame, the effect requests a ref-counted camera pullback that scales with the stack’s depth in the scene — computed against a fixed reference distance, because computing it against the current distance turns your camera into a positive feedback loop.

Safari, again, but worse

On Highway Rush we learned that Safari compiles and reflects shaders on first draw with no persistent cache — a multi-second main-thread stall the first time an effect appears. A slot game is the pathological case for this: almost every interesting visual is conditionally spawned. Win bursts, dissolve variants, cluster highlights, floating win amounts, grid-boost overlays — none of it renders during the loading screen naturally, so all of it stalls mid-celebration on a player’s first win. The worst possible moment. Our dissolve shader alone — four passes, times shadow, light, fog, and dissolve keyword combinations — put roughly 194 shader programs through first-use uniform reflection at once, at about 3ms per uniform on WebKit versus about 0.1ms on Chrome. First cluster win on iOS: a three-second freeze.

The fix grew into a proper engine service. A shader warmup system stages prefabs under a hidden transform, moves it in front of the real camera for three frames during boot, and holds the webfront’s splash screen until every scheduled pass completes. Two details turned out to matter far more than the basic idea:

  • The warmup must produce the same pipeline state the gameplay draw will. Unity’s render pipeline picks shader variants from global lighting state at render time — how many additional lights, which cascades, fog on or off. Warm up too early, or in the wrong part of the scene, and you compile pristine variants of shaders the game will never use, then stall on the real ones anyway. So the service waits about a second for scene lighting to stabilize, and stages geometry at a gameplay anchor — the grid root — so warmup symbols sit in the same light volume as real ones and the pipeline resolves the same lighting keywords.
  • Each spawning system registers its own warmup. The win handler warms its popups (actually showing one so the text-outline keyword toggles), the dissolve effect warms its burn variant half-dissolved so both branches of the fragment shader execute, the boost manager warms its overlays. New engine systems warm themselves on startup, so the next game gets the coverage for free instead of maintaining a fragile central list.

Even that was not quite enough — per-prefab warmup does not cover the combination of states a real win produces. So the boot sequence ends with a fake spin: three real grid symbols play a full win-then-dissolve cycle behind the splash screen, with a warmup flag that turns the sound and web-event calls into no-ops so the webfront never knows it happened. The single most hazardous frame of the session — the first cluster win — gets pre-played where nobody can see it.

Safari had more lessons. WebGL caps fragment shaders at 16 texture units, and feature-rich toon shaders blow past it — Unity logs a link error but keeps rendering, silently dropping the shadow-receiving variant. Objects look fine and simply never show shadows, which took a while to stop blaming on light settings. Everything that needs to receive shadows now uses a minimal custom toon shader that binds three textures.

Fonts were their own saga. TextMesh Pro’s dynamic atlas mode — the convenient one, where glyphs rasterize on demand — calls FreeType, which is a null function pointer in a WebGL wasm build. Works perfectly in the editor, breaks in the browser. So every font in the game is a static SDF atlas with its glyphs pre-baked: currency symbols for every market baked into the Latin font, and the CJK fonts for localized bonus text baked down from 3–5 MB of raw font files to atlases of exactly the seventeen Chinese and fourteen Japanese characters the translation table actually uses, with the source font reference cut so the build drops the file entirely.

Free spins turn off the sun

Farm Pop’s bonus does not cut to a different scene — the same farm transitions from day to night in front of you. One environment controller lerps everything at once: directional light color and intensity, the ambient spherical-harmonics gradient, fog, the toon shader’s shadow tint, even per-symbol emission and outline colors so the pets stay readable in the dark. Then the night lights come on: lanterns, window glow, fireflies.

Nineteen point lights is a lot for WebGL, and the way it is affordable is an unusual renderer split: the project ships three render pipeline configurations — Forward for mobile, Forward+ for desktop, and deferred for the WebGL build, where many small lights are close to free. But the lights still caused the best bug of the project: a visible flicker on the ground plane whenever the wild symbol appeared. The wild dog carries a real light for its glow, and on the big ground plane it competed with all the night lights for per-object light slots — as the sorted list reshuffled each frame, the ground popped. The fix was rendering layers: the ground got its own layer, the nineteen night lights were excluded from it entirely, and only the wild’s glow light opts in. The non-obvious part is that light layers are a shader feature, not a free engine feature — our hand-written environment shader had to add the light-layers variant and test each light’s layer mask inside its own light loop before any of it worked.

Shadows got the same kind of platform-specific care: shadow distance doubled to 100 (the portrait camera pullback had pushed the camera past the old range, silently killing shadows on phones), and the WebGL renderer runs three cascades split so the middle cascade covers exactly the band of world the grid sits in — better effective shadow resolution with no increase in shadowmap size.

The multiplier patch that grows

One mechanic is pure Farm Pop: grid boosts. Cells accumulate “+N” multiplier levels as wins land on them, and each boosted cell grows a little ecosystem — a patch of grass that gains blades with every level (10 blades at +1, capped at 55) and butterflies whose color escalates through tiers as the number climbs. Blade positions are hashed from the cell’s world position, so every cell’s patch is unique but identical every time it regrows. The overlays are pooled like everything else, with an explicit teardown on return — an early bug had a level-9 overlay going back to the pool with fifty grass blades still attached, ready to reappear fully grown on some other cell’s +1.

It is a small system, but it is the game’s personality in miniature: a math-engine integer arriving over the bridge and becoming something that feels grown rather than displayed.

One engine, thinner games each time

Farm Pop is the first of the “Pop” series, and it shares its engine with the “Rush” runners — same web bridge, same pooling, same warmup service, same build hardening. The whole slot layer (grid manager, tumble choreography, anticipation, win presentation, overlay UI, day/night system, boost manager) lives in the shared engine. What is actually Farm Pop? Ten scripts, about 1,300 lines: the animator adapter, the farmer’s costume randomizer, the butterflies and grass, the wild dog’s glow. The next game — different animals, different theme — reuses everything else.

Even the 2D art pipeline leans on this. An editor tool renders the actual 3D symbol prefabs to transparent PNGs for the paytable and marketing — posing them mid-jump by sampling animation clips in edit mode, on an isolated layer with its own throwaway camera, no Play button involved. The paytable cannot drift from the game because the paytable is the game, photographed.

That was the bet with this architecture, and Farm Pop is the proof: the second game in a genre should mostly be art.

If you want to see the result, it is live: Farm Pop on Stake.

Built with Unity 6 (WebGL) on the Raw Fun Gaming engine, for Stake Engine.

More from the studio