To practice my Python skills, I implemented a proof of concept for a
game engine server that exposes a renderer and an
entity-component-system (ECS) architecture through a RESTful API.
Fundamentally, it manages state and orchestrates concurrent operations
across subsystems for rendering, scripting, and networking, separating
data (components) from behavior (systems) to keep the codebase modular
and extensible. Flask is used for actor control and VisPy for
GPU-accelerated rendering, with everything bundled into a monolithic
application. As a proof of concept, it implements the essentials: a few
API endpoints, one to create an entity, one to add a component to a
given entity, and one to remove a given entity.
The motivation behind this project was to understand how game engines
handle the separation of concerns between game logic, rendering, and
state management. Traditional object-oriented approaches to game
development often lead to deeply nested inheritance hierarchies where a
Player class might inherit from Character,
which inherits from GameObject, which inherits from
Renderable, and so forth. This becomes unwieldy as the game
grows. The entity-component-system pattern addresses this by treating
entities as simple integer identifiers and attaching components to them
as needed. An entity that needs to be rendered gets a
Renderer component; an entity that needs custom behavior
gets a Script component; an entity that has a position in
3D space gets a Transform component. This
composition-over-inheritance approach makes it trivial to create new
entity types without modifying existing code.
The image shows the addition of an entity: a rotating cube system
custom implemented via a server request; it is subsequently removed by a
follow-up API call.

Architecture and Threading
Model
The server architecture presented several challenges, particularly
around threading and state synchronization. Flask runs the HTTP server
in one thread while VisPy’s rendering loop executes in the main thread,
and these two subsystems must coordinate access to the shared entity
state. The solution I implemented uses a global world_lock
that guards all operations on the ECS database. Whenever the API
creates, modifies, or removes an entity, it acquires this lock;
similarly, the rendering loop acquires the lock when iterating over
entities to render them. This prevents race conditions where, for
instance, the renderer might attempt to read a component from an entity
that the API is currently deleting.
In addition to the basic Flask server, I integrated Flask-SocketIO to
enable WebSocket communication for real-time updates. This allows
clients to subscribe to entity state changes and receive notifications
immediately rather than polling the HTTP endpoints. The WebSocket
handlers use the same locking mechanism to ensure thread safety. For
example, the request_status WebSocket handler acquires the
world lock before checking the server state and emitting a response.
Component System and
Script Execution
The component system is built on top of the esper ECS
library, which provides the low-level primitives for creating entities
and attaching components. I defined three primary component types:
Transform for position and scale, Script for
custom Python scripts, and Renderer for 3D mesh data. Each
component is a simple dataclass with no behavior; all logic lives in the
API handlers and the script execution system.
The Script component is particularly interesting because
it allows arbitrary Python code to be attached to entities and executed
at specific lifecycle points. When a script is added to an entity, the
server dynamically loads the Python file and checks for two special
functions: on_load, which runs once when the script is
attached, and on_update, which runs every frame at 60 FPS.
The on_update function receives a timer event and can
modify the entity’s components, including its transform. To implement
this, I use VisPy’s Timer class to schedule the callback
and store a reference to both the timer and the script module in a
global list. This design enables behavior like rotating a cube or moving
an entity along a path to be defined entirely in user-provided Python
scripts without modifying the engine code.
One challenge I encountered was ensuring that transformations applied
via the API are correctly propagated to the VisPy mesh objects. When a
Transform component is added or modified, the corresponding
mesh must be updated on the next frame. To handle this, I maintain a
global meshes list where each entry contains the entity ID,
the file path, the VisPy mesh object, and a toBeTransformed
flag. The script’s on_update function checks this flag and,
if set, applies the transformation to the mesh and clears the flag. This
deferred update pattern prevents the API thread from directly
manipulating VisPy objects, which would violate thread safety since
VisPy expects all rendering operations to occur on the main thread.
3D Rendering with VisPy
For rendering, I chose VisPy, a Python library that provides
GPU-accelerated visualization using OpenGL. VisPy’s scene graph
abstraction makes it straightforward to load 3D models and apply
transformations. When a Renderer component is added to an
entity, the server reads the mesh file using VisPy’s
io.read_mesh function, which supports formats like OBJ,
FBX, and GLTF. The vertices, faces, and normals are then used to create
a Mesh visual, which is added to the VisPy scene as a child
of the view.
Each mesh is associated with a MatrixTransform, which
allows position, rotation, and scale to be applied. When the
Transform component is updated, the mesh’s transformation
matrix is recomputed by calling translate and
scale methods on the transform object. This approach is
efficient because VisPy batches these operations and applies them in a
single draw call during the rendering loop.
Lessons and Challenges
Throughout this project, I learned the importance of explicit
synchronization when multiple threads share mutable state. Initially, I
attempted to use fine-grained locks for individual components, but this
led to deadlocks when one operation needed to modify multiple components
atomically. Switching to a single coarse-grained lock simplified the
implementation and eliminated the deadlock issues, though it does limit
concurrency. For a production game engine, a more sophisticated
approach, such as a lock-free data structure or a message-passing
architecture, would be necessary to scale to hundreds of concurrent
operations.
Another challenge was validate API inputs to prevent malformed data
from corrupting the entity state. For example, the
Transform component requires position and scale arrays with
exactly three elements, and scale values must be positive. I implemented
validation functions for each component type that check these invariants
and return meaningful error messages when validation fails. This catches
mistakes early and provides a better developer experience when using the
API.
The project repository is available at this link, and the code
is free to use under a permissive license. While this is a proof of
concept rather than a production-ready engine, it demonstrates how ECS
architectures can be exposed via HTTP APIs and how Python can be used
for both game logic and rendering.