DEV Community

Cover image for Backend Engineer (Me) Ships a Browser Game With One Unintentional System Requirement: My Monitor

Backend Engineer (Me) Ships a Browser Game With One Unintentional System Requirement: My Monitor

Giorgi Kobaidze on August 09, 2026

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. Prefer Watching Instead? If reading isn't your thing,...
Collapse
 
annavi11arrea1 profile image
Anna Villarreal

I remember this one! This is the one where I didnt realize I actually had to shift to move. Haha!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

I totally remember your comment. I was like: β€œoh no, do I have a bug somewhere?β€πŸ˜„

Then I realized it was by designπŸ˜„

Collapse
 
annavi11arrea1 profile image
Anna Villarreal

πŸ˜‚πŸ˜‚πŸ˜‚

Collapse
 
unitbuilds profile image
UnitBuilds

240hz isnt even the limit tbh, with 1kHz monitors, the new 'standard' for gaming monitors being up from 144hz to 240hz, with the 'high refresh rate' being 300hz+, it's a bit of a tricky one. My advice, always decouple rendering from the gameloop, else you end up with issues like the old Bionicle game, where if you werent running on a potato, it runs at unplayable speeds.

For perspective, with my current endeavor on the gaming front, I am building Dwarven Stronghold (Think Dwarf Fortress, but written in Rust, so it's not so slow and heavy to run), One of the first things I did, was build the framework for the game, specifically separating the render loop from the simulation loop, so the simulation loop runs as fast as it wants, with the game-loop running at a fixed 'game time : wall clock time', so the sub-second simulations are variable depending on the overhead, while keeping the rendering separate, so the rendering isnt limited by the game's loop. So you can keep 240hz or even 1000hz, even if the game drops it's simulation to 10 TPS. The separation of concerns allows the game to stay smooth, even when the gameloop fluctuates.

Think COD, if you play online and your network drops, you still run around, still shoot, etc. Then disconnect after it times, so it doesnt interrupt the gameplay for minor stutters.

Collapse
 
gnlassi profile image
Ghulam Nabi

Totally agree. Decoupling render loop from sim loop is the way to go for high refresh rates.
240hz/1000hz monitors are useless if your game logic is tied to FPS.
Fixed timestep for simulation + variable render = smooth gameplay even at 10 TPS.
Great example with Dwarven Stronghold in Rust!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

That makes sense πŸ’―

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Such an insightful comment. That just opens up more things to research for me as a beginner in the game development area.

Thanks a lot!πŸ™

Collapse
 
unitbuilds profile image
UnitBuilds

Anytime, if you're looking in to game-states, I'd highly recommend looking up merkle roots and how you can use them to track state propagations. It makes sure that anomalies are pruned and that the state is always preserved. If you use hardware AES processing, it's also practically free, because it uses a part of the processor games dont generally use. Just be mindful of how dense you make it, keep it transactional with pointers, not full-on state dumping, else you'll hit OOM in no time.

Thread Thread
 
georgekobaidze profile image
Giorgi Kobaidze

This is great. Next time I’ll decide to write a game, I’ll definitely get back to your comments!

Collapse
 
hiper2d profile image
Aliaksei Zelianouski • Edited

Same path here - backend habits, no game-dev background, and I also ship a browser game. Although it's just a chat, no fancy visuals. The same bug shape lives one layer up though: the game is tuned against specific AI models, a provider ships a new version, and the bots behave differently on identical code.

The game looks awesome. After the re-tune, does it still feel like the game you built, or are you driving a slightly different car on your own 165Hz now?

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Good to know I’m not the only one.

It feels fine now, but it took quite a lot of tuning and testing. Totally worth it, though.

Collapse
 
mindmagic profile image
Mindmagic

Hi, there!
I am looking for a partner to collaborate with by sharing an account.
In return, you will receive a 20–30% share;
I hope this collaboration leads to a long-term partnership.
WhatsApp: +1 (910) 852-7435
Telegram: @bytepil0t

Collapse
 
hadil profile image
Hadil Ben Abdallah

So cool! 😍
Great job!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thank you!😊

Collapse
 
nyaomaru profile image
nyaomaru

It's great idea using articles as a billboard and I enjoyed night driving 🚘

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

I know, it’s real fun! πŸš—

Collapse
 
publiflow profile image
PubliFlow

Good technical content. I'd love to see more exploration of the edge cases and failure modes β€” understanding when and why these patterns break down is often more valuable than knowing how to implement them.

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks a lot. It really had me dive DEEP into some low-level stuff.

Collapse
 
_hm profile image
Hussein Mahdi

Great write-up, the part most "just multiply by dt" tutorials skip is exactly the part you nailed: decay and lerp need Math.pow, not linear scaling. The 0.97 * dt = 1.94 example makes the failure mode click instantly.

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks a lot. At first I just went for the dt multiplication but I quickly realized it wasn’t gonna cut it.

Collapse
 
mindmagic profile image
Mindmagic

Hi, there!
I am looking for a partner to collaborate with by sharing an account.
In return, you will receive a 20–30% share;
I hope this collaboration leads to a long-term partnership.
WhatsApp: +1 (910) 852-7435
Telegram: @bytepil0t

Collapse
 
tyriantrade profile image
Tyrian Trade

Really enjoyed this deep dive. The β€œmy monitor was secretly a system dependency” part made me laugh, but the technical lesson is actually a great one.

I especially liked the distinction between value += rate * dt, exponential decay with Math.pow(factor, dt), and frame-rate-independent lerp. It’s easy to think β€œjust multiply everything by delta time” solves the problem, but smoothing and decay require a bit more care.

This is also a good reminder that performance bugs aren’t always obvious bugs β€” sometimes the code works perfectly on the developer’s machine while behaving like a completely different application elsewhere. Great write-up!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks a lot. This is another reminder that sometimes things turn out to be way more complex than they seem.

Collapse
 
edwardsinclair profile image
Edward Sinclair

This is exactly why I like weekend challenges. πŸ˜„ The goal isn’t always to build something perfect, it’s to force yourself into territory you normally wouldn’t touch. A backend engineer deciding to build a browser game in two days sounds like the perfect recipe for chaos and learning. Looking forward to seeing how this turned out!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

It really was pretty chaoticπŸ˜„ I almost failed to finish itπŸ˜„

Collapse
 
louis7 profile image
Louis Liu

Interesting game, definitely worth playing! πŸ˜†

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks! Enjoy!πŸ˜„

Collapse
 
mindmagic profile image
Mindmagic

Hi, there!
I am looking for a partner to collaborate with by sharing an account.
In return, you will receive a 20–30% share;
I hope this collaboration leads to a long-term partnership.
WhatsApp: +1 (910) 852-7435
Telegram: @bytepil0t

Collapse
 
talha_ramzan_3878156fea8c profile image
Talha Ramzan

This is a really clean explanation of delta time, the lerp formula
especially, most people just do alpha * dt and don't realize it breaks
down at higher dt values.
Bookmarking this for the next time I touch anything with requestAnimationFrame.

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks a lot! I tried my best.πŸ™

Collapse
 
glenallen profile image
Glen Allen • Edited

The interesting part is that the bug wasn't really in the physics formula, it was in an unstated assumption about the environment. That's a pattern that shows up far beyond games: hardware, browser behavior, timing, concurrency, and configuration can quietly become dependencies. Making those assumptions explicit and testing across them is often what separates "works locally" from software that's actually portable.

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Exactly! And that’s the reason why game development is so complicated and tricky. I mean this one was a simple game, nothing complicated in it conceptually, but still, a major issue showed up. Granted, I’m still a rookie in this regard, but still.

Collapse
 
siya_eduonix profile image
Siya Jain

This is such a relatable β€œworks on my machine” bug πŸ˜‚ The 165Hz monitor secretly becoming a system requirement is hilarious, but the explanation of delta time and why simply adding * dt isn’t enough makes this genuinely useful. Great write-up!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thank you!πŸ˜„

Collapse
 
kenielzep97 profile image
Self-Correcting Systems

The exponential decay one is the part worth the whole article and i think its sharper than you let on.

you say the naive fix, speed *= 0.97 * dt, is wrong. it is, but its a worse class of wrong than the bug it replaces. the original bug was at least monotonic. everybody got a slower car than you intended, consistently, scaled by their refresh rate. annoying but predictable. the naive fix flips the sign. at dt of 2 you get 1.94 and the car accelerates when it drives off road, so a 30fps player gets the opposite of the mechanic. you didnt just fail to fix it, you built a machine that behaves backwards on the exact hardware that was already suffering most.

and thats the trap in the pattern, its that * dt genuinely works on case one. you learn the rule from the linear case, it rewards you, then you carry it into the multiplier case and it quietly inverts. a fix that works the first three times you try it is how you end up shipping the fourth one without checking.

good writeup. the fact that your own monitor was an untracked variable in every test you ran is the part id put on a sticker.

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Hm… interesting insight I’ll have another look sometime in the future. Thank you!

Collapse
 
nicola_fiore_89b1628cd6af profile image
Nicola Fiore

😍😍

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

✨✨

Collapse
 
unified_mentor profile image
Unified Mentor

Great Game!

Collapse
 
georgekobaidze profile image
Giorgi Kobaidze

Thanks a lot!πŸ™

Collapse
 
tech_grundy profile image
The Tech Grundy

"Works on my machine" taken to the ultimate physical level: Works on my monitor!

As a backend developer, it is so easy to forget how chaotic the frontend display ecosystem is compared to server environments where parameters are fixed. Hardcoding coordinate logic based on your own screen resolution is a rite of passage every engineer goes through at least once. Rebuilding the canvas loop with proper aspect ratio scaling and resolution independence is a great recovery. Hilarious writeup!

Such a great post-mortem! Canvas coordinate scaling and frame-rate independence catch almost everyone building their first browser game.

Going from hardcoded pixel offsets to a normalized coordinate space (or logical resolution scaled to the viewport) is one of those architectural shifts that feels so satisfying once implemented. The lesson here on separating game-state logic from rendering coordinates is spot on. Thanks for sharing the honest breakdown!

Collapse
 
matlho profile image
matthieu

this sound like 'to the mad' ''p
is purely, i like that,, new stuff is now overclock for an idea own who missed a support to work, i take it c[_]

Collapse
 
mindmagic profile image
Mindmagic

Hi, there!
I am looking for a partner to collaborate with by sharing an account.
In return, you will receive a 20–30% share;
I hope this collaboration leads to a long-term partnership.
WhatsApp: +1 (910) 852-7435
Telegram: @bytepil0t