A WebGL-based TypeScript game engine focusing on 2D/3D rendering with modern web technologies.
-
Rendering System
- WebGL context management
- GLSL shader compilation and linking
- Vertex buffer management
- Material-based rendering
- Sprite system with textures
- Color tinting support
- UV coordinate mapping
-
Resource Management
- Reference-counted textures and materials
- Asynchronous asset loading
- Automatic resource cleanup
- Material and texture caching
- Memory optimization through sharing
- Hierarchical loading system
-
Mathematics
- Matrix4x4 transformations
- Local/world space transforms
- Vector2/3 operations
- Orthographic projection
- Transform hierarchy calculations
- Parent-child transformations
-
Core Architecture
- Component-based system
- Scene graph hierarchy
- Zone management system
- Message bus communication
- Asset management pipeline
- Game loop optimization
- State machine integration
-
Graphics Features
- Material system
- Texture unit management
- Mipmap generation
- Color tinting
- Sprite transformations
- Component-based rendering
- Attribute handling
-
Scene Management
- Hierarchical scene graph
- Zone-based level system
- Resource lifecycle management
- Object transformation inheritance
- Component lifecycle handling
- Scene state management
- Add texture support
- Implement camera system
- Add basic 2D sprite rendering
- Implement basic physics
- Add input handling
- Implement scene graph
- Add asset loading system
- Add texture atlasing
- Implement sprite batching
- Add mipmap support
- Implement texture compression
Built with:
- TypeScript
- WebGL
- Bun v1.2.3 (for building and development)
How WebGL Works:
- JavaScript Control: WebGL is controlled through JavaScript, which sends commands to the browser's GPU.
- Rendering Pipeline: WebGL utilizes a rendering pipeline, where vertices, shaders, and other data are processed by the GPU to create 3D graphics.
- Shaders:
Shaders, written in
GLSL, define how objects are drawn, including their appearance, lighting, and other visual effects. - GPU Acceleration: The GPU processes the data and shaders, rendering the 3D scene efficiently and providing high-performance graphics.
- Display:
The rendered 3D graphics are then displayed within the HTML5
<canvas>element.
Main engine class orchestrating the game loop and rendering pipeline.
Responsibilities:
- Manages WebGL context and rendering pipeline
- Controls game loop timing and execution
- Handles shader program lifecycle
- Manages vertex buffer operations
- Responds to window resize events
Example:
const engine = new KoruTSEngine();
engine.start(); // Initializes WebGL and starts game loopStatic utility class for WebGL context management.
Responsibilities:
- Handles canvas element creation and setup
- Initializes WebGL context with error checking
- Provides fallback handling for WebGL support
- Manages context attributes and extension
Example
const canvas = GLUtilities.initialize(); // Creates canvas & WebGL contextManages vertex buffer objects and attribute configurations.
Responsibilities:
- Creates and manages VBOs
- Handles different data types (Float32, Int16, etc.)
- Configures vertex attribute layouts
- Manages buffer memory and uploads
- Supports multiple drawing primitives
Example
const buffer = new GLBuffer(3); // 3 components per vertex (x,y,z)
buffer.addAttributeLocation(new AttributeInfo(0, 3, 0)); // position attribute
buffer.pushBackData([0.0, 0.5, 0.0, ...]); // Add vertex data
buffer.upload(); // Send to GPUGLSL shader program management and compilation.
Responsibilities:
- Compiles vertex and fragment shaders
- Links shader programs
- Manages uniform locations
- Handles attribute bindings
- Provides error checking and logging
Example
const shader = new Shader("basic", vertexShaderSource, fragmentShaderSource);
shader.use(); // Activate shader for renderingHandles 4x4 matrix operations for 3D transformations and projections.
Responsibilities:
- Creates and manages 4x4 transformation matrices
- Provides orthographic projection matrix generation
- Handles column-major matrix operations
- Supports WebGL-compatible array format
Matrix Structure
// Column-Major Order:
[
2/(r-l), 0, 0, -(r+l)/(r-l),
0, 2/(t-b), 0, -(t+b)/(t-b),
0, 0, 2/(n-f), -(f+n)/(n-f),
0, 0, 0, 1
]Example:
// Create orthographic projection for 1920x1080 screen
const projection = Matrix4x4.orthographic(
0, // left
1920, // right
0, // bottom
1080, // top
-1, // near clip
100 // far clip
);Represents a 3D vector with x, y, z components for spatial operations.
Responsibilities:
- Stores 3D coordinates and directions
- Provides vector math operations
- Supports position and scaling operations
- Converts to WebGL-compatible formats
Vector Components:
class Vector3 {
private _x: number; // X component
private _y: number; // Y component
private _z: number; // Z component
}Common Uses:
- Object positions
- Movement directions
- Scale factors
- Force vectors
- Normal vectors
Example:
// Create a position vector
const position = new Vector3(100, 200, 0);
// Access components
position.x = 150; // Move right
position.y += 10; // Move up
// Convert to array for WebGL
const vertexData = position.toFloat32Array();Central message distribution system implementing the Publisher/Subscriber pattern.
Responsibilities:
- Message distribution and queuing
- Priority-based message processing
- Handler subscription management
- Decoupled component communication
Example:
// Subscribe to messages
MessageBus.addSubscription("COLLISION", this);
// Post a message
MessageBus.post(new Message("COLLISION", this, { damage: 50 }));
// Process queued messages
MessageBus.update(gameTime);Singleton system for managing game assets and resource loading.
Responsibilities:
- Asset loading and caching
- Loader type resolution
- Resource lifecycle management
- Loading state tracking
Example:
// Register a custom loader
AssetManager.registerLoader(new TextureLoader());
// Load an asset
AssetManager.loadAsset("player.png");
// Get a loaded asset
const texture = AssetManager.getAsset("player.png");Handles WebGL texture loading and management.
Responsibilities:
- Asynchronous texture loading
- WebGL texture lifecycle management
- Texture unit binding
- Mipmap generation
- Placeholder texture during load
Example:
// Create and load a texture
const texture = new Texture("player.png");
// Bind for rendering
texture.activateAndBind(0); // Use texture unit 0Singleton system for texture resource management.
Responsibilities:
- Reference counting for textures
- Automatic resource cleanup
- Texture caching
- Memory optimization
Example:
// Get or create a texture (increments reference count)
const texture = TextureManager.getTexture("player.png");
// Release when done (decrements reference count)
TextureManager.releaseTexture("player.png");Combines textures and colors for rendering configuration.
Responsibilities:
- Texture and color tint management
- Reference counting integration
- WebGL uniform configuration
- Resource lifecycle handling
Example:
// Create material with texture and blue tint
const material = new Material(
"crate",
"assets/textures/crate.jpg",
new Color(0, 128, 255, 255)
);
// Use in sprite
sprite.setMaterial(material);Singleton system for material resource management.
Responsibilities:
- Material caching and reuse
- Reference counting for materials
- Automatic cleanup of unused materials
- Memory optimization
Example:
// Register a new material
MaterialManager.registerMaterial(material);
// Get existing material (increments reference)
const material = MaterialManager.getMaterial("crate");
// Release when done
MaterialManager.releaseMaterial("crate");RGBA color representation with WebGL compatibility.
Responsibilities:
- Color component management (RGBA)
- WebGL-compatible format conversion
- Common color preset support
- Alpha transparency support
Example:
// Create custom color
const purple = new Color(128, 0, 128, 255);
// Get WebGL format
const glColor = purple.toFloat32Array();
// Use preset
const white = Color.white();// Vertex Shader
attribute vec3 a_position;
void main() {
gl_Position = vec4(a_position, 1.0);
}
// Fragment Shader
precision mediump float;
void main() {
gl_FragColor = vec4(1.0);
}// Added projection and model calculations to the vertex shader
attribute vec3 a_position;
uniform mat4 u_projection;
uniform mat4 u_model;
void main() {
gl_Position = u_projection * u_model * vec4(a_position, 1.0);
};
// Basic fragment shader
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
};src/
├── core/
│ ├── assets/
│ │ ├── assetManager.ts # Asset loading and management
│ │ ├── IAsset.ts # Asset interface definition
│ │ ├── IAssetLoader.ts # Asset loader interface
│ │ └── imageAssetLoader.ts # Image loading implementation
│ ├── graphics/
│ │ ├── texture.ts # WebGL texture management
│ │ ├── textureManager.ts # Texture reference counting
│ │ ├── material.ts # Material definition & management
│ │ ├── materialManager.ts # Material reference counting
│ │ ├── color.ts # RGBA color management
│ │ └── sprite.ts # 2D sprite rendering
│ ├── message/
│ │ ├── messageBus.ts # Message distribution system
│ │ ├── message.ts # Message class definition
│ │ ├── IMessageHandler.ts # Message handler interface
│ │ └── messageSubscriptionNode.ts # Message queue node
│ ├── math/
│ │ ├── matrix4x4.ts # Matrix transformations
│ │ ├── vector2.ts # 2D vector operations
│ │ └── vector3.ts # 3D vector operations
│ ├── gl/
│ │ ├── gl.ts # WebGL context management
│ │ ├── glBuffer.ts # Buffer operations
│ │ └── shaders/ # Shader implementations
│ │ ├── shaders.ts # Base shader class
│ │ └── basicShader.ts # Basic material shader
│ └── engine.ts # Main engine class
├── shaders/
│ ├── basic.vert.ts # Basic vertex shader source
│ └── basic.frag.ts # Basic fragment shader source
├── index.html # Entry point
├── app.ts # Application setup
└── tsconfig.json # TypeScript config
- Install dependencies:
bun install- Build the project:
bun run build- Open
index.htmlin a browser.
// Initialize engine
const engine = new KoruTSEngine();
// Create and load a textured sprite
const sprite = new Sprite("player", "assets/player.png");
sprite.load();
// Position sprite
sprite.position.x = 100;
sprite.position.y = 200;
// Start the engine
engine.start();Changes:
-
Shader Management
- Added complete shader pipeline:
private loadShaders(): void { let vertexShaderSource = ` attribute vec3 a_position; void main() { gl_Position = vec4(a_position, 1.0); }`; let fragmentShaderSource = ` precision mediump float; uniform vec4 u_color; void main() { gl_FragColor = u_color; }`; }
-
Buffer Management
- Added GLBuffer class with:
- Vertex buffer creation and management
- Attribute handling
- Data upload to GPU
- Drawing functionality
- Added GLBuffer class with:
-
Triangle Rendering
- Implemented basic triangle rendering:
private createBuffer(): void { let vertices = [ // x, y, z 0.0, 0.0, 0.0, // bottom-left 0.0, 0.5, 0.0, // top-left 0.5, 0.5, 0.0 // top-right ]; }
This PR establishes the foundation for 2D rendering in the KoruTS engine. Core Changes:
-
Matrix4x4 Class
- Implemented orthographic projection matrix
- Added column-major matrix operations
- Documentation for matrix transformations
-
GLBuffer Improvements
- Added vertex buffer management
- Implemented attribute handling
- Added support for different data types
- Enhanced buffer binding operations
-
Sprite System
- Added basic Sprite class
- Implemented vertex buffer creation for sprites
- Added size and position management
- Set up draw operations
-
Engine Updates
- Integrated sprite rendering pipeline
- Added projection matrix support
- Implemented viewport management
- Added window resize handling
Example Sprite creation and rendering:
const sprite = new Sprite("test", 100, 100); sprite.load(); // Sets up vertex buffer sprite.draw(); // Renders using WebGL
The PR establishes a robust foundation for asset management and component communication in the engine.
-
Asset Management System
- Implemented
AssetManagersingleton for centralized resource management - Added asset loading pipeline with support for different asset types
- Created
IAssetandIAssetLoaderinterfaces - Implemented image asset loading with
ImageAssetLoader - Added asset caching and state management
- Implemented
-
Messaging System
- Added
MessageBusfor decoupled component communication - Implemented priority-based message processing
- Created message subscription and handling system
- Added support for HIGH and NORMAL priority messages
- Implemented message queuing for NORMAL priority
- Added
-
Integration
- Connected asset loading with message system for load notifications
- Added asset load completion messaging
- Implemented message-based asset state updates
This PR implements texture loading and management with reference counting:
-
Texture Management
- Added
TextureManagersingleton for centralized texture handling - Implemented reference counting for automatic cleanup
- Added texture caching to prevent duplicate loading
// Get a texture (creates or increments reference) const texture = TextureManager.getTexture("player.png"); // Release when done (decrements reference) TextureManager.releaseTexture("player.png");
- Added
-
Asset Loading System
- Implemented asynchronous texture loading
- Added message-based load notifications
- Created placeholder white texture during load
// Load texture asynchronously AssetManager.loadAsset("player.png"); // Listen for load completion Message.subscribe("ASSET_LOADED", this);
-
Sprite System Updates
- Added texture coordinate support
- Implemented UV mapping for sprites
- Added texture binding in render pipeline
const sprite = new Sprite("player", "player.png"); sprite.load(); // Sets up vertices and UVs sprite.draw(); // Binds texture and renders
-
WebGL Integration
- Added texture parameter configuration
- Implemented texture unit management
- Added texture uniform support in shaders
This PR implements a complete material system with color tinting and texture management:
-
Material Management
- Added MaterialManager singleton for centralized handling
- Implemented reference counting for materials
- Added material caching to prevent duplicates
// Register a new material MaterialManager.registerMaterial(new Material( "crate", "assets/textures/crate.jpg", new Color(0, 128, 255, 255) )); // Get material (increments reference) const material = MaterialManager.getMaterial("crate");
-
Color System
- Added Color class for RGBA management
- Implemented WebGL-compatible color formats
- Added common color presets
// Create custom color with alpha const tint = new Color(255, 128, 0, 255); // Orange // Convert for WebGL use const glColor = tint.toFloat32Array();
-
Sprite Integration
- Updated sprites to use materials
- Added color tinting support
- Implemented material reference management
// Create sprite with material const sprite = new Sprite("player", "crate"); sprite.load(); // Material color affects rendering sprite.draw(shader); // Uses material's texture and tint
-
Shader Updates
- Added material uniform support
- Implemented texture sampling
- Added color tinting in fragment shader
// Fragment shader with material support uniform vec4 u_tint; // Material color uniform sampler2D u_diffuse; // Material texture void main() { gl_FragColor = texture2D(u_diffuse, v_texCoord) * u_tint; }
-
Resource Management
- Automatic cleanup of unused materials
- Reference counting for textures and materials
- Memory optimization through shared resources
// Material cleanup MaterialManager.releaseMaterial("crate"); // Decrements reference
This PR implements a robust scene graph and component system for game object management:
-
Scene Graph Implementation
- Added hierarchical object relationships
- Implemented parent-child transformations
- Added scene management system
// Create parent-child relationship const parent = new SimObject(1, "parent"); const child = new SimObject(2, "child"); parent.addChild(child); // Child inherits parent's transformations parent.transform.rotation.z = Math.PI / 4; // 45 degrees
-
Component System
- Implemented component-based architecture
- Added base component class
- Created sprite component
// Add sprite component to object const player = new SimObject(1, "player"); const spriteComponent = new SpriteComponent("render", "playerTexture"); player.addComponent(spriteComponent);
-
Zone Management
- Added Zone class for level management
- Implemented ZoneManager singleton
- Added zone state machine
// Create and switch to new zone const levelId = ZoneManager.createZone("Level1", "First Level"); ZoneManager.changeZone(levelId);
-
Transform System
- Implemented local and world space transforms
- Added matrix hierarchy calculations
- Created transform component
// Transform operations gameObject.transform.position.x = 100; gameObject.transform.rotation.z = Math.PI / 2; gameObject.transform.scale.x = 2;
-
Resource Management
- Added scene-level resource tracking
- Implemented component lifecycle management
- Added hierarchy-based loading system
// Resources load through hierarchy scene.load(); // Loads all objects and components
This PR implements a robust component system with JSON serialization and zone management:
-
Core Component Architecture
- Implemented
IComponentinterface for all game components - Created
BaseComponentabstract class with lifecycle methods - Added component ownership tracking through
SimObject
class HealthComponent extends BaseComponent { public update(time: number): void { // Custom update logic } }
- Implemented
-
Component Factory System
- Created
IComponentBuilderinterface for JSON deserialization - Implemented
ComponentManagersingleton for builder registration - Added type-safe component instantiation from JSON
// Register builder ComponentManager.registerBuilder(new SpriteComponentBuilder()); // Create from JSON const component = ComponentManager.extractComponent({ type: "sprite", material: "player" });
- Created
-
JSON Serialization System
- Added
setFromJsonmethods to core math classes (Vector2/3, Transform) - Implemented
IComponentDatainterface for serializable components - Created JSON asset loader with error handling
// Vector3 deserialization const vec = new Vector3(); vec.setFromJson({ x: 1, y: 2, z: 3 }); // Transform deserialization transform.setFromJson({ position: { x: 10, y: 5 }, rotation: { z: 45 }, // degrees scale: { x: 2, y: 2 } });
- Added
-
Zone Management System
- Implemented recursive
loadSimObjectfor hierarchical scene loading - Added zone state machine (UNINITIALIZED → LOADING → UPDATING)
- Created JSON-based zone initialization
// Zone JSON structure { "id": 1, "name": "Forest", "objects": [{ "name": "Player", "components": [{ "type": "sprite", "material": "player" }] }] }
- Implemented recursive
-
Engine Integration
- Connected
AssetManagerwithZoneManagerfor resource loading - Implemented message-based zone transitions
- Added initialization to engine startup
// Engine startup sequence start() { AssetManager.initiliaze(); ZoneManager.initialize(); ComponentManager.registerBuilder(new SpriteComponentBuilder()); ZoneManager.changeZone(0); // Load first zone }
- Connected
Key Features:
- Type-safe component creation from JSON
- Recursive scene graph construction
- Asynchronous zone loading
- Clean separation between data and behavior
- Comprehensive error handling
The system enables data-driven game object creation while maintaining type safety and providing clear lifecycle management.
This PR implements a flexible behaviour system with JSON serialization and management:
-
Core Behaviour Architecture
- Implemented
IBehaviourinterface for all game behaviours - Created
BaseBehaviourabstract class with lifecycle methods - Added behaviour ownership tracking through
SimObject
class RotationBehaviour extends BaseBehaviour { public update(time: number): void { this._owner.transform.rotation.add(this._rotation); } }
- Implemented
-
Behaviour Factory System
- Created
IBehaviourBuilderinterface for JSON deserialization - Implemented
BehaviourManagersingleton for builder registration - Added type-safe behaviour instantiation from JSON
// Register builder BehaviourManager.registerBuilder(new RotationBehaviourBuilder()); // Create from JSON const behaviour = BehaviourManager.extractBehaviour({ type: "rotation", rotation: { x: 0, y: 1, z: 0 } });
- Created
-
JSON Serialization Integration
- Added behaviour data classes implementing
IBehaviourData - Implemented
setFromJsonfor behaviour configuration - Created validation for required behaviour properties
// Behaviour data deserialization const data = new RotationBehaviourData(); data.setFromJson({ name: "spin", rotation: { x: 0, y: 2, z: 0 } }); // JSON behaviour definition { "type": "rotation", "name": "coinSpin", "rotation": { "y": 5 } }
- Added behaviour data classes implementing
-
Engine Integration
- Connected
BehaviourManagerwith core engine systems - Implemented automatic builder registration
// Engine initialization start() { // ...existing code BehaviourManager.registerBuilder(new RotationBehaviourBuilder()); // Additional system initialization }
- Connected
This PR implements a comprehensive animation and input management system with component-based architecture:
-
Animation System Core
- Created
AnimatedSpriteclass extendingSpritewith frame-by-frame animation - Implemented
IMessageHandlerfor animation event communication - Refactored
Spriteclass to use extensibleVertextype system
- Created
class DuckAnimation extends AnimatedSprite {
public update(time: number): void {
this.playAnimation("quack");
}
}-
Component Architecture
- Built
AnimatedSpriteComponentfor entity animation management - Developed
KeyboardMovementBehaviourfor input-driven motion - Added
setData()andclearData()methods for state management
- Built
// Animation component registration
ComponentManager.registerBuilder(new AnimatedSpriteComponentBuilder());
// Movement behaviour from JSON
const movement = BehaviourManager.extractBehaviour({
type: "keyboardMovement",
speed: 2.5
});-
Input Management System
- Implemented
InputManagerclass for unified input handling - Added keyboard and mouse event listeners
- Integrated input system with message bus
- Implemented
// Input configuration
InputManager.registerKeyMap({
"MoveUp": "KeyW",
"MoveLeft": "KeyA",
"MoveRight": "KeyD",
"MoveDown": "KeyS"
});-
Engine Integration & Optimization
- Added update/render helper methods to streamline game loop
- Implemented delta time handling in
MessageBusandZoneManager - Added texture blending for transparent backgrounds
// Engine initialization
start() {
InputManager.initialize();
MessageBus.subscribe("MOUSE_CLICK", this);
// Register animation builders
ComponentManager.registerBuilder(new AnimatedSpriteComponentBuilder());
}-
Asset Pipeline
- Added duck texture with transparent background
- Created dedicated material for animated sprites
- Implemented vertex-based rendering pipeline
// Example animated sprite configuration
{
"type": "animatedSprite",
"material": "duck",
"frameWidth": 64,
"frameHeight": 64,
"frameCount": 8,
"frameSequence": [0,1,2,3,4,5,6,7]
}This PR introduces a comprehensive collision detection system with shape-based intersection testing and component architecture:
-
Core Collision Components
- Created
CollisionComponentclass with builder pattern for JSON serialization - Implemented
IShape2Dinterface as foundation for collision shapes - Developed
Rectangle2DandCircle2Dclasses implementing intersection logic
- Created
// Collision component registration
ComponentManager.registerBuilder(new CollisionComponentBuilder());
// Example collision configuration
const collision = new CollisionComponent({
shape: "rectangle",
width: 32,
height: 32
});-
Shape Mathematics
- Implemented dynamic vertex calculation for shape transformations
- Added distance calculation between vector points
- Created origin-based collision detection system
// Rectangle intersection check
const rect1 = new Rectangle2D(width, height);
const rect2 = new Rectangle2D(width, height);
const isColliding = rect1.intersects(rect2);-
Collision Management System
- Built
CollisionManagerclass to handle game object collisions - Integrated collision system into engine update loop
- Implemented optimized intersection algorithms
- Built
// Collision system initialization
start() {
CollisionManager.initialize();
MessageBus.subscribe("COLLISION_EVENT", this);
}-
Vector Mathematics
- Added clone methods for Vector2 objects
- Fixed distance calculation between vectors
- Implemented helper methods for shape transformations
// Vector operations
const v1 = new Vector2(10, 10);
const v2 = v1.clone();
const distance = Vector2.distance(v1, v2);-
JSON Configuration Support
- Added type-safe collision data serialization
- Implemented builder pattern for component creation
- Created test zone with collision configuration
// Example collision configuration
{
"type": "collision",
"shape": "circle",
"radius": 16,
"offset": { "x": 0, "y": 0 }
}-
Engine Integration
- Refactored component directory structure
- Added collision system to core update loop
- Implemented origin calculation within shapes
// Engine update loop
update(time: number) {
CollisionManager.update(time);
// ... other systems
}