The Engine Heartbeat
Episode 3 builds the game loop—the engine heartbeat that drives everything. We implement separate update and render loops with delta time and fixed tick rates, then wrap it all in the @ges/core package for clean imports.
Making It Move
After Episode 2, we had a renderer. We could draw a rectangle to the canvas. But it was static—just sitting there, frozen in time. No animation. No updates. No life.
A game engine without a game loop is like a car without an engine. You've got all the parts, but nothing to make them go.
Episode 3 fixed that.
Two Loops, One Heartbeat
The core of any game engine is the game loop. But here's the thing: you don't just need one loop. You need two.
The update loop handles logic—moving entities, checking collisions, processing input. It needs to run at a fixed rate so physics stays consistent.
The render loop handles drawing. It runs as fast as the browser allows (typically 60fps via requestAnimationFrame), but it's decoupled from updates so you can have smooth visuals even if logic runs slower.
This separation is critical. If you tie rendering to logic, you get inconsistent behavior across different frame rates. That's how you end up with games where physics break on high-refresh monitors.
Delta Time and Fixed Ticks
Both loops use delta time—the time elapsed since the last tick. This keeps everything frame-rate independent.
The update loop runs at a desired tick rate (240 Hz in our case). If the loop runs faster than that, it waits. If it runs slower, it catches up by running multiple updates in one frame.
The render loop targets 60fps but adapts based on what the browser can handle. It uses a smoothing factor to avoid jittery FPS readings.
The Core Package
We also introduced @ges/core in this episode. It's a thin wrapper that re-exports everything from the engine and renderer packages.
Why bother? Because it gives us a clean API surface. Instead of importing from multiple packages, you just import from @ges/core. It's a single entry point that hides the internal structure.
This also means we can reorganize packages later without breaking every import in the editor.
Dependency Flow
The structure now looks like this:
- Editor → imports from
@ges/core - @ges/core → re-exports
@ges/engineand@ges/renderer - @ges/engine → orchestrates the game loop
- @ges/renderer → handles Canvas 2D drawing
Clean. Modular. Easy to reason about.
Seeing It Run
At the end of Episode 3, we hooked the engine up to the editor. Now instead of a static rectangle, we have a running engine that updates and renders on every frame.
You can see the FPS counter in the corner. You can watch the tick rate stay locked at 240 Hz. The engine is alive.
That's the milestone. We went from "able to draw" to "able to run." Everything else—entities, components, systems—builds on top of this foundation.
What's Next
Now that we have the engine loop, we need something for it to manage. That's where the Entity Component System comes in. Episode 4 introduces the ECS architecture—the pattern that drives modern game engines.