GameFrame
- 1 Devlogs
- 8 Total hours
A 3d game engine that runs in your browser!
A 3d game engine that runs in your browser!
Devlog-1:
Making the canva for video games!!!
I wanted this engine to run natively in the browser without massive downloads, so I chose:
React + Vite for the frontend UI and state management.
Three.js & React-Three-Fiber (R3F) for the 3D rendering pipeline.
Cannon-es (@react-three/cannon) for rigid-body physics.
Zustand for lightweight, blazing-fast state management.
React Flow (@xyflow/react) for the Visual Scripting node editor.
Most React apps map data to UI components linearly, but game engines need an Entity-Component System (ECS) to handle hundreds of objects dynamically. I built a custom ECS using Zustand.
// The core engine state powering the ECS
interface EngineState {
entities: Record<string, Entity>;
addEntity: (name: string) => string;
addComponent: (entityId: string, componentType: ComponentType) => void;
updateComponent: (entityId: string, componentType: ComponentType, data: any) => void;
}
This allows the engine’s Outliner and Inspector panels to dynamically edit any object in the scene in real-time, just like Unity or Unreal.
The coolest feature I got working today was the Visual Script Editor. I used React Flow to let users connect nodes (like “On Update” -> “Move To Player”). But how does a node graph become executable game?
I wrote a compiler that traverses the node graph and translates it into a raw JavaScript string. This string is saved into the entity’s script component and executed dynamically in the game loop using a
Function constructor!
// A snippet of the compiler output injected into the game loop
} else if (node.data.label === 'Enemy: Move To Player') {
output += `
if (api.velocity) {
const toPlayer = camera.position.clone().sub(mesh.position);
toPlayer.y = 0;
toPlayer.normalize();
api.velocity.set(toPlayer.x * 4, -1, toPlayer.z * 4);
}
`;
}
The biggest hurdle today was the physics loop. Initially, the enemy tracking script updated the Zustand state every frame. With 15 enemies running at 60fps, it was triggering 900 React state updates a second, completely freezing the browser!
The novel solution for this was bypassing React’s render lifecycle entirely for physics. Instead of updating the state.entities transform data, the compiled scripts now directly tap into the raw Cannon-es physics
API (api.velocity.set()), mutating the physics hull natively. The performance skyrocketed from a frozen screen to a buttery smooth 60fps.