Devlog 2, right on schedule. I got quite a bit done this week. Most significantly, I finished a huge performance refactor and the game now reliably runs 60 FPS even on lower end devices in later rounds. Here are more details:
| before | after | |
|---|---|---|
| Smoothness | 925 dropped frames (74%) | 0 dropped frames — locked to refresh, 0 long tasks |
| Actual presentation FPS | 31.5 | 60.1 |
| Main thread busy | 99.9% (idle 5 ms / 10.3 s) | 43.5% (idle 5.7 s / 10.0 s) |
| JS self-time | 6,738 ms | 2,336 ms (−65%) |
| GC pauses | 642 ms · 210 scavenges · 5,139 MB young garbage | 90 ms · 41 scavenges · 1,106 MB (−86% pause, −78% garbage) |
| React renders | 5,629 | 1,582 (−72%) |
motion.div re-renders |
×1,837 | ×246 (−87%) |
| Long tasks (>50 ms) | 4 (up to 71 ms) | 0 |
| Forced reflow | 45.4 ms · 131 forced layouts | 16.6 ms · 67 (−63%) |
Very satisfying. Whenever starting on performance work, there’s usually a seed of doubt in my mind about whether I’ll actually see real gains. In this case, there were obvious opportunities to optimize. Up until this point I had been ignoring performance completely, and focusing on prototyping the first version of all the systems.
As part of this performance work, I fixed memory leaks and UI refresh rate issues, but there was one obvious opportunity: React Pixi. My entire game renderer is built with React bindings for PixiJS. This was great for code organization and personal familiarity. But React comes with a lot of overhead, and extracting data every frame from the sim and passing it to React to render was inefficient. I rewrote all the components to imperatively update directly from the simulation’s state, skipping React lifecycle and external store syncing entirely. In future projects, I will skip PixiJS React completely and use PixiJS directly.
Normally, debugging performance in web apps involves a lot of staring at a view sort of like this until my eyes bleed:

But after years of suffering, I have finally had enough of flame charts. In order to make performance investigation easier, I built a tool called perftale and you can find it now on GitHub.
Here are some scraps from Taos.

Every creature has a leap animation now!

A few more animation sketches.

We’ve started redesigning all the Lords. Taos’s take on the Lich is on the left, compared to my original sketch on the right.
After some playtesting with my brothers, I realized that goal scoring needs to be a lot more cinematic. Sports are about the highlights, and goal score highlights need to be exciting. Before this week, the orb carrier AI kind of cowered in the corner or ran against invisible walls.
I reworked creature AI inside of goals, added a knockback effect upon scoring, and made tackles no longer cancel the goal timer. By the way, ignore the white overlay that shows up when scoring. That’s going to be a beast that emerges from the pool to eat the goal scorer soon.

Here is the screenshot of the week. I’m playing my brother Yarrow here, and I’m about to win with the Lizard lord. At the moment Lizard has a very over-powered ability where his terrain spells cannot be overriden by the opponent. I overwhelmed Yarrow with negative terrain and won on the next round. Next week, I’m going to release full redesigns of every Lord.

I’m getting closer to finishing a draft of the Orb Ball website.
Join the discord – I’ll be organizing test matches.