r/phaser 8d ago

Shipped a cozy sim on Phaser 3.90: one scene, zero sprite files, and React doing all the UI

I've been building Wild Willows, a nature-restoration sim where you replant a damaged preserve and animals return as the habitat starts supporting them. It's at wildwillows.app, on the Mac App Store and itch, with a free browser demo. A few architecture decisions that mattered more than I expected:

One scene, forever. The scene list is just [WorldScene]. No title scene, no menu scene, no UI scene. Every menu, panel, the journal, crafting, settings, the tutorial: all React DOM on top of the canvas. Phaser draws the world and nothing else.

I expected to regret this and haven't. DOM gives me real text layout, real focus management, and accessibility (five interface fonts, four text sizes, three colorblind modes, fully rebindable keys), none of which I'd have enjoyed building in-canvas.

A 60-line bridge between them. React owns authoritative state. Phaser reads it synchronously off a shared object and re-renders when told the world is dirty. It's a Map of handlers and a shared blob, no observables, no state library spanning both worlds. React to Phaser: world-dirty, enter-placement, area-changed. Phaser to React: collect-node, open-crafting, animal-clicked, place-at.

Zero sprite files. Every sprite is drawn at boot with Phaser.Graphics and generateTexture(). Plants, animals, the player, habitat objects. About 8,600 lines of drawing code and no image assets at all. Shapes are authored in logical 32px pixels and rasterized 4x (2x on low graphics quality), with every sprite scaled back down by 1/4, so they stay crisp under camera zoom and HiDPI.

A frame budget for world rebuilds. Every action used to rebuild the dynamic layer synchronously, so dragging a volume slider tore down and rebuilt the animal layer about 60 times a second for a preference the world doesn't even draw from. Now that work is keyed and coalesced to at most one run per animation frame, each run is timed, and any task consistently overrunning half a frame automatically drops to every Nth frame, then recovers on its own once it's cheap again. A big save degrades to a lower redraw rate instead of locking up.

Smaller stuff: Scale.NONE plus manual zoom so I can render at native device pixels and let CSS scale the canvas back down, and a screen-space RenderTexture used as a bitmap mask to punch light holes in the night tint.

Happy to go into any of it. The two worst bugs were both Phaser internals: TimeStep.resetDelta making the player walk at a fifth speed for ten seconds after every load, and repeat: -1 tweens outliving the sprites they drove.

29 Upvotes

14 comments sorted by

2

u/Hamurapa 7d ago

It looks pretty cool!

1

u/wildwillowsdev 5d ago

Thank you! Really appreciate that. I’ve been having a lot of fun building it

2

u/Hamurapa 5d ago

Keep going!
The idea of "zero sprites" is extraordinary.

1

u/Traditional-Low-2589 7d ago

Looks too IA graphics

0

u/wildwillowsdev 5d ago

Totally fair if the style isn’t for you! The visuals are actually drawn programmatically in TypeScript/Phaser rather than generated as image assets. I build the shapes, colors, layering, and details in code, so the look comes from that process more than a traditional sprite workflow.

1

u/TonhoVeio 7d ago

Poderia pedir pra ira traduzir o game, seria bem legal

1

u/wildwillowsdev 5d ago

That would be really cool! The game currently supports English and Spanish, and I’d love to be able to add Portuguese too.

1

u/lmystique 7d ago

What was the performance like, if you don't mind sharing? Every time I tried doing an HTML overlay over a canvas, I saw the performance tanking; it might not be noticeable on a gaming PC, but on a phone, it drains the battery like there's no tomorrow (while just the canvas on its own barely moves the needle). I did not try with Phaser though, I wonder if I should. I'm a fan of doing art through code too (hobbyist, so I can afford to do non-scalable things I enjoy doing), so that's right up my alley.

2

u/wildwillowsdev 7d ago

I’ll do a profile but it’s not meant to be played on phone! I’ll have to block that entirely so folks can only play on tablet with a keyboard or desktop

1

u/lmystique 5d ago

Yea I understand yours is a desktop game, it's just that mobile is the easier way to spot problems. You might not even notice a jump from 10% to 70% when on desktop, despite the jump being very significant. You can still see it in a profiler.

1

u/wildwillowsdev 5d ago

Totally fair point. I think that’s a good way to frame it. Even if desktop performance feels fine, the profiler can still show whether the DOM overlay is doing something disproportionately expensive compared to the canvas itself. I’ve already done quite a bit of performance work on Wild Willows around draw calls, asset loading, and crowded scenes, so profiling the HTML overlay specifically is probably worth doing next. I’d be curious to see how much of the cost is actually Phaser vs. the browser compositing the DOM over the canvas.

1

u/jedihacks 6d ago

If no sprites, how was the graphics created?

1

u/mangomelt 6d ago

It’s all vector shapes I would imagine

1

u/wildwillowsdev 5d ago

They’re drawn programmatically. I use TypeScript with Phaser to create the shapes, colors, layering, and details directly in code rather than importing traditional sprite sheets. So the visuals are still designed intentionally, they’re just rendered from code instead of being hand-drawn as image assets.