Doomberg-Machine

🔧 Doomberg Machine - Technical Documentation

This document provides comprehensive technical information for developers who want to understand, modify, or extend the Doomberg Machine codebase.

📋 Table of Contents

🎯 Project Overview

Doomberg Machine is a browser-based 2D physics puzzle game built with vanilla JavaScript and the Matter.js physics engine. Players create Rube Goldberg machines to doom an NPC character through physics-based interactions.

Project Goals

🛠️ Technology Stack

Core Technologies

Development Tools

Runtime Requirements

🏗️ Architecture

High-Level Architecture

┌─────────────────────────────────────┐
│         User Interface              │
│  (HTML/CSS - Controls & Canvas)     │
└────────────┬────────────────────────┘
             │
┌────────────▼────────────────────────┐
│      Game Controller (game.js)      │
│  - Event Handling                   │
│  - State Management                 │
│  - Object Placement                 │
└────────────┬────────────────────────┘
             │
┌────────────▼────────────────────────┐
│    Physics Engine (Matter.js)       │
│  - Body Simulation                  │
│  - Collision Detection              │
│  - Constraint System                │
└─────────────────────────────────────┘

Design Patterns Used

  1. Module Pattern: Self-contained game initialization
  2. Observer Pattern: Event-driven collision detection
  3. Factory Pattern: Object creation in placeObject()
  4. State Machine: Game state management (ready/running/doomed)

📁 Code Structure

File Organization

Doomberg-Machine/
├── index.html          # Main HTML structure
├── style.css           # All styling and animations
├── game.js             # Game logic and physics integration
├── matter.min.js       # Matter.js physics engine (minified)
├── package.json        # Project metadata and dependencies
├── README.md           # User-facing documentation
└── docs/              # GitHub Pages documentation
    ├── gameplay.md    # Player guide
    ├── technical.md   # This file
    ├── architecture.md # Design documentation
    └── gameplan.md    # Enhancement roadmap

Key Files

index.html

style.css

game.js

🎮 Core Systems

1. Initialization System

function init() {
    // Engine creation
    // Renderer setup
    // World initialization
    // Static objects (ground, walls)
    // NPC creation
    // Event listener setup
    // Collision detection registration
}

Key Responsibilities:

2. Object Placement System

function placeObject(type, x, y) {
    // Factory pattern for creating different object types
    // Store original position for reset functionality
    // Add to physics world
    // Track in placedObjects array
}

Supported Object Types:

3. State Management

Game States:

State Variables:

let isRunning = false;      // Physics simulation status
let isPaused = false;       // Pause state (timeScale = 0)
let isSlowMotion = false;   // Slow-motion state (timeScale = 0.25)
let isGridEnabled = false;  // Grid overlay and snap-to-grid state
let npcDoomed = false;      // Victory condition flag
let selectedTool = null;    // Currently selected object type
let placedObjects = [];     // Array of placed body references
let placedConstraints = []; // Array of constraint references

// Scoring system state
let gameStartTime = 0;      // Timestamp when machine starts running
let doomTime = 0;           // Time in seconds from start to doom
let collisionCount = 0;     // Total collisions during simulation
let currentScore = 0;       // Most recent calculated score
let currentStars = 0;       // Most recent star rating (1-3)

4. Collision Detection System

Events.on(engine, 'collisionStart', (event) => {
    // Check if NPC is involved in collision
    // Verify sufficient velocity for "doom"
    // Trigger doom sequence if conditions met
});

Doom Criteria:

Velocity Calculation:

const velocity = Math.abs(otherBody.velocity.x) + Math.abs(otherBody.velocity.y);

5. Playback Control System

Simulation Speed Control:

Speed Calculation:

function applyTimeScale() {
    if (isPaused) {
        engine.timing.timeScale = 0;           // Paused
    } else if (isSlowMotion) {
        engine.timing.timeScale = 0.25;        // 25% speed
    } else {
        engine.timing.timeScale = 1.0;         // Normal speed
    }
}

Features:

6. Scoring System

Score Calculation: The scoring system evaluates player performance across four metrics, with a combo multiplier applied to the total.

Metrics:

  1. Success Bonus (1000 points): Base reward for dooming the NPC
  2. Efficiency Bonus (0-500 points): Rewards using fewer objects
    • Formula: max(0, 500 - ((objectCount - 1) × 20))
    • 1 object = 500 points
    • Each additional object reduces bonus by 20 points
  3. Speed Bonus (0-500 points): Rewards faster completion
    • Formula: max(0, 500 - (doomTime × 50))
    • At 0 seconds = 500 points
    • Every 1 second increase in doomTime reduces the bonus by 50 points (e.g., 0.8s → 460, 1s → 450, 2s → 400)
  4. Variety Bonus (0-600 points): Rewards using different object types
    • Derived from current placed objects at doom time
    • Each unique type = 100 points
    • Maximum 600 points (all 6 types)
  5. Combo Multiplier (1.0x-1.6x): Rewards chain reactions
    • Activated at 5+ collisions
    • Formula: 1.1 + min((collisionCount - 5) × 0.05, 0.5)
    • Maximum 1.6x multiplier

Star Rating Calculation:

let stars = 1;
if (score >= 2000) stars = 2;
if (score >= 2800) stars = 3;

Tracking:

Display:

7. Reset System

Reset Functionality:

Original Position Storage:

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

8. Sound System

Audio Architecture: The game uses the Web Audio API to generate simple sound effects programmatically without requiring audio files. This provides a lightweight solution with zero external dependencies.

Sound Types:

Implementation:

const audioContext = new (window.AudioContext || window.webkitAudioContext)();

function playSound(type) {
    if (!soundEnabled) return;
    
    const oscillator = audioContext.createOscillator();
    const gainNode = audioContext.createGain();
    
    // Configure based on sound type
    // Create tones using oscillators with frequency/gain envelopes
}

Sound Events:

User Preferences:

⚙️ Physics Engine

Matter.js Integration

Engine Configuration:

engine = Engine.create();
world = engine.world;
world.gravity.y = 1;  // Standard gravity

Renderer Configuration:

render = Render.create({
    canvas: canvas,
    engine: engine,
    options: {
        width: 1200,        // CANVAS_WIDTH
        height: 600,        // CANVAS_HEIGHT
        wireframes: false,
        background: '#87CEEB'  // Matter.js handles background fill
    }
});

Grid Constants:

Important: The canvas element should NOT have a CSS background as it will render over the canvas drawing context. Matter.js’s background option fills the canvas properly before rendering physics bodies.

Physics Properties

Density

Restitution (Bounciness)

Static vs Dynamic Bodies

NPC Positioning Constants

The NPC is positioned to stand on the ground from initialization to prevent physics phase-through issues:

9. Save/Load System

Architecture:

Save Data Structure:

{
    version: 1,                    // Format version for future compatibility
    timestamp: 1708034567890,      // Unix timestamp (ms)
    name: "My Awesome Machine",    // User-provided name
    objects: [
        {
            type: "ball",
            x: 259.5,
            y: 190.2,
            angle: 0
        },
        {
            type: "ramp",
            x: 358.0,
            y: 289.5,
            angle: -0.297  // Radians
        },
        {
            type: "seesaw",
            x: 557.0,
            y: 388.0,
            angle: 0,
            seesawId: 0    // For complex objects
        }
        // ... more objects
    ]
}

Storage Keys:

Features:

Complex Object Handling:

Error Handling:

Limitations:

Constraints

Seesaw Constraint:

const constraint = Matter.Constraint.create({
    bodyA: pivot,      // Static pivot point
    bodyB: plank,      // Dynamic plank
    length: 0,         // Zero length = fixed rotation point
    stiffness: 0.9     // High stiffness for realistic pivot
});

🔄 Game Loop

Rendering Loop

Matter.js handles the game loop internally through the Runner and Renderer:

// Continuous rendering (always active)
Render.run(render);

// Physics simulation (activated on "Run Machine")
Runner.run(runner, engine);

Frame Rate: 60 FPS (Matter.js default)

Update Order:

  1. Process user input events
  2. Update physics simulation (when running)
  3. Detect collisions
  4. Update visual representation
  5. Render frame

📚 API Reference

Core Functions

init()

Initializes the game engine, renderer, world, and event listeners.

createNPC()

Creates the NPC as a compound body with multiple parts positioned on the ground.

placeObject(type, x, y)

Factory function for creating and placing game objects.

deleteObjectAtPosition(x, y)

Deletes object at specified position using Matter.js Query.

applyExplosionForce(explosionX, explosionY, explosionRadius, explosionForce)

Applies radial force to all nearby objects from an explosion center.

undo()

Reverts the most recent action in history.

redo()

Re-applies a previously undone action.

recordAction(action)

Records an action in the history for undo/redo.

updateUndoRedoButtons()

Updates the enabled/disabled state of undo/redo buttons.

runMachine()

Activates physics simulation and makes NPC dynamic.

resetMachine()

Restores all objects to original positions.

clearAll()

Removes all placed objects from the world.

deleteObjectAtPosition(x, y)

Finds and deletes the object at the specified canvas position.

deleteObject(body)

Removes a body from the world, handling compound objects (seesaws).

doomNPC()

Triggers the victory condition when NPC is hit.

calculateAndDisplayScore()

Calculates and displays the final score based on performance metrics.

showScoreModal(score, stars, breakdown)

Displays the score modal with detailed breakdown.

togglePause()

Toggles simulation pause state.

toggleSlowMotion()

Toggles slow-motion mode (25% speed).

applyTimeScale()

Applies appropriate timeScale based on current state.

toggleGrid()

Toggles the grid overlay and snap-to-grid functionality.

loadContraption(name)

Loads a contraption design from localStorage.

listSavedContraptions()

Gets list of all saved contraption names.

deleteContraption(name)

Deletes a saved contraption from localStorage.

refreshSavedList()

Updates the saved designs dropdown menu.

getObjectType(body)

Helper function to determine object type from a Matter.js body.

Utility Functions

normalizeAngle(angle)

Normalizes an angle to the range [0, 2π).

snapToGrid(x, y)

Snaps coordinates to the nearest grid intersection when grid is enabled.

drawGrid(context)

Draws the grid overlay on the canvas.

rotateRamp(angleChange)

Updates the current ramp rotation angle.

updateStatus(message)

Updates the status display text.

Event Handlers

Mouse Click Handler

canvas.addEventListener('click', (event) => {
    // Convert screen coordinates to canvas coordinates
    // Apply snap-to-grid if enabled
    // Call placeObject() with selected tool
});

Keyboard Handler

document.addEventListener('keydown', (event) => {
    // Q: Rotate ramp counter-clockwise
    // E: Rotate ramp clockwise
});

Button Click Handlers

🛠️ Development Guide

Setup Instructions

  1. Clone the repository:
    git clone https://github.com/MW-GC/Doomberg-Machine.git
    cd Doomberg-Machine
    
  2. Install dependencies:
    npm install
    
  3. Run a local server: ```bash

    Option 1: Python

    python3 -m http.server 8080

Option 2: Node.js

npx http-server -p 8080

Option 3: PHP

php -S localhost:8080


4. **Open in browser**:
Navigate to `http://localhost:8080`

### Adding New Object Types

To add a new object type, follow this pattern:

1. **Add button to HTML** (`index.html`):
```html
<button class="tool-btn" data-tool="spring">🌀 Spring</button>
  1. Add case to placeObject() (game.js): ```javascript case ‘spring’: body = Bodies.circle(x, y, 15, { restitution: 1.5, // Super bouncy! density: 0.02, render: { fillStyle: ‘#9B59B6’ // Purple } }); break;

case ‘explosive’: body = Bodies.circle(x, y, 18, { restitution: 0.3, density: 0.06, label: ‘explosive’, // For collision detection render: { fillStyle: ‘#E74C3C’ // Red } }); break;


3. **Test thoroughly**:
- Place multiple instances
- Test physics interactions
- Verify reset functionality
- Check collision detection

### Modifying Physics Parameters

**Gravity Adjustment**:
```javascript
world.gravity.y = 1.5;  // Increase for stronger gravity

Doom Threshold:

if (velocity > 5) {  // Require faster impact
    npcDoomed = true;
    doomNPC();
}

Object Properties:

// Make balls heavier and less bouncy
body = Bodies.circle(x, y, 20, {
    restitution: 0.5,    // Reduce from 0.8
    density: 0.08,       // Increase from 0.04
    render: {
        fillStyle: '#FF6B6B'
    }
});

Coding Conventions

Naming Conventions:

Code Style:

State Management:

Browser Compatibility

Supported Browsers:

Required Features:

🧪 Testing

Manual Testing Checklist

Object Placement:

Ramp Rotation:

Physics Simulation:

Collision Detection:

Reset/Clear:

Performance Testing

Metrics to Monitor:

Performance Tips:

🚀 Deployment

GitHub Pages Setup

  1. Enable GitHub Pages:
    • Go to repository Settings
    • Navigate to Pages section
    • Select source branch (usually main)
    • Select root folder
  2. Configure Jekyll (optional): Create _config.yml:
    theme: jekyll-theme-cayman
    title: Doomberg Machine
    description: Build Rube Goldberg machines of doom!
    
  3. Verify Deployment:
    • Check https://username.github.io/repository-name
    • Ensure all assets load correctly
    • Test on mobile devices

Production Optimization

Recommended Optimizations:

  1. Minify JavaScript (game.js)
  2. Minify CSS (style.css)
  3. Optimize image assets (if added)
  4. Enable gzip compression on server
  5. Add cache headers for static assets

Build Script Example:

{
  "scripts": {
    "build": "uglifyjs game.js -o game.min.js",
    "deploy": "npm run build && git push"
  }
}

🐛 Debugging

Common Issues

Issue: Canvas shows background gradient but no game objects

Issue: NPC disappears when simulation starts

Issue: Objects fall through ground

Issue: NPC doesn’t doom

Issue: Reset doesn’t work

Issue: Canvas coordinates incorrect

Debug Tools

Enable Matter.js Debug Renderer:

render = Render.create({
    // ... other options
    options: {
        wireframes: true,  // Show collision boundaries
        showAngleIndicator: true,
        showVelocity: true
    }
});

Console Logging:

// Log collision events
Events.on(engine, 'collisionStart', (event) => {
    console.log('Collision:', event.pairs);
});

// Log object placement
function placeObject(type, x, y) {
    console.log(`Placing ${type} at (${x}, ${y})`);
    // ... rest of function
}

📖 Additional Resources

External Documentation

Learning Resources


Version: 1.0.0
Last Updated: February 2026
Maintainers: MW-GC Team

For questions or contributions, please open an issue on GitHub!