I just shipped alpha v0.17 of Eidolon, a browser-based isometric action RPG built entirely with JavaScript and Three.js. No game engine, no Unity WebGL export—just vanilla JS and a lot of math.
It's still early, but it's playable. Here's what I've learned building it so far.
Why Three.js for a 2.5D Game?
Most isometric games use 2D sprite engines. I went with Three.js because:
- Real lighting — Dynamic shadows and ambient occlusion without faking it
- Camera flexibility — Easy to add zoom, rotation, and cinematic effects
- Future-proofing — Can add 3D elements without rewriting everything
The tradeoff? You're managing a full 3D scene for what looks like a 2D game.
The Architecture
The game runs on a custom Entity Component System (ECS):
class Entity {
constructor() {
this.id = crypto.randomUUID();
this.components = new Map();
}
addComponent(component) {
this.components.set(component.constructor.name, component);
return this;
}
getComponent(type) {
return this.components.get(type.name);
}
}
Systems iterate over entities each frame:
class MovementSystem {
update(entities, delta) {
for (const entity of entities) {
const transform = entity.getComponent(Transform);
const velocity = entity.getComponent(Velocity);
if (transform && velocity) {
transform.position.x += velocity.x * delta;
transform.position.z += velocity.z * delta;
}
}
}
}
This keeps game logic decoupled from rendering and makes it easy to add new behaviors.
Performance: Hitting 60 FPS on Mobile
The biggest challenge was performance. Here's what worked:
1. Aggressive frustum culling
Only render what the camera can see:
const frustum = new THREE.Frustum();
frustum.setFromProjectionMatrix(
camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse)
);
for (const entity of entities) {
entity.mesh.visible = frustum.containsPoint(entity.position);
}
2. Instanced meshes for repeated objects
Trees, rocks, and enemies use InstancedMesh to batch draw calls:
const mesh = new THREE.InstancedMesh(geometry, material, COUNT);
3. Object pooling
Never allocate during gameplay. Projectiles, particles, and effects all come from pre-allocated pools.
GLTF Asset Pipeline
All 3D models are loaded as GLTF:
const loader = new GLTFLoader();
async function loadModel(url) {
const gltf = await loader.loadAsync(url);
return gltf.scene;
}
I use Blender for modeling, export to .glb, and compress with gltf-transform to keep file sizes small.
What's Next for v1.0
The alpha is playable but there's a lot left to build:
- More enemy types and boss encounters
- Inventory and loot system
- Procedural dungeon generation
- Sound design and music
Play the Alpha
You can play Eidolon v0.17 here: eidolon.mendola.tech
Source code: github.com/aeml/eidolon
It's rough around the edges, but I'd love feedback. Let me know what breaks.
Building games in the browser is more viable than ever. Three.js handles the heavy lifting, and modern JavaScript is fast enough for real-time games. If you're thinking about it—just start.
Got questions? Find me at mendola.tech.
Top comments (0)