Doomberg-Machine

🏛️ Doomberg Machine - Architecture & Design

This document describes the architectural decisions, design patterns, and system design of the Doomberg Machine project.

🎯 Design Philosophy

Core Principles

  1. Simplicity First: Keep the codebase minimal and readable
  2. Physics-Driven: Let the physics engine handle complexity
  3. Immediate Feedback: Provide instant visual and textual feedback
  4. Extensibility: Design for easy addition of new features
  5. Browser-Native: Use web standards, minimize dependencies

Design Goals

🏗️ System Architecture

Layer Architecture

┌─────────────────────────────────────────────┐
│         Presentation Layer                  │
│  - HTML Structure (index.html)              │
│  - CSS Styling (style.css)                  │
│  - Visual Feedback & Animations             │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│         Application Layer                   │
│  - Game State Management                    │
│  - Event Handling & User Input              │
│  - Object Factory & Placement               │
│  - UI Control Logic                         │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│         Physics Layer                       │
│  - Matter.js Engine                         │
│  - Collision Detection                      │
│  - Body Simulation                          │
│  - Constraint System                        │
└─────────────────────────────────────────────┘

Component Diagram

┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│   Canvas    │────▶│    Render    │────▶│   Physics    │
│             │     │   Manager    │     │   Engine     │
└─────────────┘     └──────────────┘     └──────────────┘
       │                                          │
       │                                          │
       ▼                                          ▼
┌─────────────┐                          ┌──────────────┐
│   Mouse     │                          │  Collision   │
│  Controller │                          │  Detector    │
└─────────────┘                          └──────────────┘
       │                                          │
       │                                          │
       ▼                                          ▼
┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│  Placement  │────▶│    State     │◀────│    Doom      │
│   System    │     │   Manager    │     │   System     │
└─────────────┘     └──────────────┘     └──────────────┘

🔄 Data Flow

Object Placement Flow

User Click Event
       │
       ▼
Mouse Coordinate Conversion
       │
       ▼
Selected Tool Check
       │
       ▼
placeObject() Factory
       │
       ├──▶ Create Physics Body
       │
       ├──▶ Store Original Position
       │
       ├──▶ Add to World
       │
       └──▶ Track in Array

Simulation Flow

Run Machine Click
       │
       ▼
Set isRunning = true
       │
       ├──▶ Make NPC Dynamic
       │
       └──▶ Start Physics Runner
              │
              ▼
       Physics Update Loop (60 FPS)
              │
              ├──▶ Apply Forces
              │
              ├──▶ Update Positions
              │
              ├──▶ Detect Collisions
              │
              │    ┌────────────┐
              │    │ Collision? │
              │    └─────┬──────┘
              │          │ Yes
              │          ▼
              │    Check Velocity
              │          │
              │          ▼
              │    Doom if Fast Enough
              │
              └──▶ Render Frame

State Transition Diagram

     ┌─────────┐
     │ INITIAL │
     └────┬────┘
          │ window.load
          ▼
     ┌─────────┐  place object  ┌─────────┐
  ┌─▶│  READY  │◀───────────────│ READY + │
  │  └────┬────┘                │ OBJECTS │
  │       │                     └─────────┘
  │       │ run machine
  │       ▼
  │  ┌─────────┐
  │  │ RUNNING │
  │  └────┬────┘
  │       │
  │       ├──────────────────┐
  │       │                  │
  │       │ collision        │ reset
  │       │ w/ velocity      │
  │       ▼                  ▼
  │  ┌─────────┐        ┌─────────┐
  │  │ DOOMED  │        │  RESET  │
  │  └─────────┘        └────┬────┘
  │                          │
  └──────────────────────────┘
                    
  clear all → READY

🎨 Design Patterns

1. Factory Pattern

Usage: Object creation in placeObject()

Benefit: Centralizes object creation logic, makes adding new types easy

function placeObject(type, x, y) {
    let body;
    
    switch(type) {
        case 'ball':
            body = createBall(x, y);
            break;
        case 'box':
            body = createBox(x, y);
            break;
        // ... more types
    }
    
    if (body) {
        configureBody(body, x, y);
        addToWorld(body);
    }
}

2. Observer Pattern

Usage: Collision detection via Matter.js events

Benefit: Decouples collision logic from simulation loop

Events.on(engine, 'collisionStart', (event) => {
    // React to collisions without polling
    handleCollisions(event.pairs);
});

3. State Machine

Usage: Game state management

States:

Transitions:

4. Memento Pattern (Simplified)

Usage: Object reset system

Implementation: Store original state on body creation

body.originalPosition = { x, y };
body.originalAngle = angle;

// Later, restore:
Body.setPosition(body, body.originalPosition);
Body.setAngle(body, body.originalAngle);

5. Strategy Pattern (Implicit)

Usage: Different physics properties per object type

Benefit: Each object type has its own “strategy” for physics behavior

const strategies = {
    ball: { restitution: 0.8, density: 0.04 },
    box: { restitution: 0.3, density: 0.05 },
    domino: { restitution: 0.1, density: 0.05 }
};

🗃️ Data Structures

Primary Data Structures

1. placedObjects: Array<Body>

let placedObjects = [];

2. placedConstraints: Array

let placedConstraints = [];

3. Matter.js World Composite

engine.world

Object Properties Schema

Standard Body

{
    position: { x: number, y: number },
    angle: number,
    velocity: { x: number, y: number },
    angularVelocity: number,
    isStatic: boolean,
    
    // Custom properties
    originalPosition: { x: number, y: number },
    originalAngle: number,
    
    // Matter.js properties
    density: number,
    restitution: number,
    friction: number,
    // ... many more
}

NPC Composite Body

{
    parts: [body, head, leftArm, rightArm, leftLeg, rightLeg],
    isStatic: boolean,
    label: 'npc',
    // ... standard body properties
}

⚙️ Physics System Design

Physics Configuration

Coordinate System:

World Boundaries:

┌────────────────────────────┐
│          Sky Area          │ ← 0
│                            │
│    ┌──────────────────┐    │
│ W  │  Play Area       │  W │
│ A  │                  │  A │
│ L  │                  │  L │
│ L  │                  │  L │
│    └──────────────────┘    │
│        Ground (20px)       │ ← 600
└────────────────────────────┘
  ↑                        ↑
  5px                      1195px

Gravity:

Collision Detection

Detection Method: Broadphase → Narrowphase (Matter.js internal)

Broadphase: SAT (Separating Axis Theorem)

Narrowphase: Precise collision resolution

Doom Detection Logic:

if (collision involves NPC && !npcDoomed) {
    otherBody = get non-NPC body;
    velocity = |otherBody.velocity.x| + |otherBody.velocity.y|;
    
    if (velocity > DOOM_THRESHOLD) {
        doom();
    }
}

Why Check Impact Velocity:

Constraint System

Seesaw Implementation:

Pivot (Static)
    
     Constraint (length: 0, stiffness: 0.9)
    
Plank (Dynamic)

Constraint Properties:

🎮 Input System Design

Mouse Input Processing

Coordinate Transformation:

// Screen coordinates → Canvas coordinates
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const canvasX = (event.clientX - rect.left) * scaleX;
const canvasY = (event.clientY - rect.top) * scaleY;

Why Needed:

Keyboard Input

Ramp Rotation System:

Angle Management:

// Continuous rotation with normalization
currentRampAngle += angleChange;
currentRampAngle = normalizeAngle(currentRampAngle);

Normalization: Keeps angle in [0, 2π) range

🎨 UI/UX Design

Visual Hierarchy

Priority Levels:

  1. Primary: Canvas (game area) - largest, centered
  2. Secondary: Control buttons - grouped logically
  3. Tertiary: Status displays - informational
  4. Quaternary: Instructions - reference material

Color Scheme

Palette:

Primary Gradient: #667eea  #764ba2 (purple)
Header Gradient: #f093fb  #f5576c (pink-red)
Sky: #87CEEB (light blue)
Ground: #8B4513 (brown)

Object Colors:

Semantic Colors:

Animation Design

Doom Pulse Animation:

@keyframes doom-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.05); }
}

Button Hover:

transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);

Accessibility

Focus States:

.tool-btn:focus, .action-btn:focus {
    outline: 3px solid #4CAF50;
    outline-offset: 2px;
}

Keyboard Navigation:

Screen Reader Support:

🔧 Performance Considerations

Optimization Strategies

1. Object Pooling (Not Implemented)

2. Constraint Separation

3. Static Object Optimization

4. Event Listener Efficiency

Performance Limits

Recommended Object Counts:

Bottlenecks:

  1. Collision detection (O(n²) worst case)
  2. Rendering complex shapes
  3. Constraint solving iterations
  4. DOM updates for status text

🚀 Extensibility Design

Adding New Object Types

Required Steps:

  1. Add button to HTML
  2. Add case to placeObject() switch
  3. Define physics properties
  4. Choose appropriate color

Optional Steps:

Potential Extensions

Feature Categories:

1. Object Types

2. Gameplay Mechanics

3. Social Features

4. Visual Enhancements

📊 System Metrics

Code Metrics

Total Lines of Code: ~1,500
- game.js: 431 lines
- style.css: 190 lines
- index.html: 61 lines
- Documentation: 800+ lines

Cyclomatic Complexity: Low-Medium
Functions: 15 primary functions
Dependencies: 1 (Matter.js)

Performance Metrics

Frame Rate: 60 FPS target
Physics Updates: 60 Hz
Render Updates: 60 Hz
Input Latency: <16ms
Memory Usage: ~30-50 MB

🔐 Security Considerations

Current Security

Safe Practices:

Not Vulnerable To:

Future Considerations

If Adding Server Features:

📚 Design Decisions Log

Key Decisions

1. Why Matter.js?

2. Why Separate Bodies/Constraints Arrays?

3. Why Check Impact Velocity?

4. Why Static NPC Until Run?

5. Why No Object Dragging?

🔮 Future Architecture

Proposed Improvements

1. Module System

// Current: Single file
// Proposed: Multiple modules
import { PhysicsEngine } from './physics.js';
import { ObjectFactory } from './objects.js';
import { UIController } from './ui.js';

2. State Management Library

// Current: Global variables
// Proposed: Centralized state
const state = {
    game: { isRunning, npcDoomed },
    objects: { placed, selected },
    ui: { status, doomStatus }
};

3. Component Architecture

// Proposed: Component classes
class GameObject {
    constructor(type, x, y) { }
    place() { }
    reset() { }
    remove() { }
}

class Ball extends GameObject { }
class Box extends GameObject { }

📖 References

Influential Designs

Technical References


Architecture Version: 1.0
Document Version: 1.0
Last Updated: February 2026

This architecture is designed to be simple, maintainable, and extensible while maintaining excellent performance and user experience.