Work plur
game

PLUR

Peace, Love, Unity, Roll — momentum-based 3D soccer-ball racer with a global leaderboard.

PLUR is a momentum-based 3D browser game where you roll a procedurally-textured soccer ball around a stadium, collecting coins and country flags, dodging hazards, and scoring goals. Three.js + WebGL on the client, vanilla PHP + SQLite serving the web portal and a global leaderboard, glassmorphism on the chrome. No framework on the front — just HTML5/CSS3/JS — and a CI/CD pipeline scaffolded for a future native C/OpenGL Windows build.

Last updateMar 21, 2026 PrimaryJavaScript
  • JavaScript
  • Three.js
  • WebGL
  • PHP
  • SQLite
  • Apache
  • Lucide
PLUR — Peace, Love, Unity, Roll — momentum-based 3D soccer-ball racer with a global leaderboard.
PLUR media
PLUR media
PLUR media
PLUR media

PLUR — Peace, Love, Unity, Roll — is a momentum-based 3D browser game where the player rolls a procedurally-textured soccer ball around a stadium, collecting coins and country flags, dodging hazards, and scoring goals. The game is rendered in Three.js / WebGL; the portal around it (landing page, leaderboard, download page, admin) is hand-rolled HTML5/CSS3/vanilla JS with a glassmorphism skin.

What makes the rolling feel right

  • Real momentum, not arcade physics — the ball decelerates on flat ground, accelerates down ramps, and carries energy through corners. Players who lean into line-choice are rewarded; players who mash forward stall out on the slow stuff.
  • Procedural ball texturing so every coin and flag pickup actually changes the look of the thing you're steering. The ball is the playable surface, not just a sprite.
  • Stadium environment with hazards that respect the ball's mass — bumpers shove, ramps redirect, goal frames let you score from clever angles. The mesh catalogue is in stadium_meshes.json; new levels are JSON edits, not engine changes.
  • Coin + flag collection drives the score; a global leaderboard at /scores.html keeps the run honest.

How it's hosted

Static front-end out of public/ on Apache. PHP + SQLite under the API in api/ for score submission, leaderboard retrieval, asset management, and admin auth — REST- ish endpoints, no framework. The whole stack runs on the same box as the rest of the Gaming World installations; deploys are rsync-and-reload. CI/CD is scaffolded for a future native Windows port (cross-compiled C/OpenGL) but the browser build is the primary surface today.

The naming

"Peace, Love, Unity, Respect" is the original PLUR — the rave-culture credo. Swapping Respect for Roll was a one-letter joke that committed itself the first time someone said it out loud. The whole project has the same register: serious physics under a name that's clearly not serious.

Straight from the source

The project's own README.

Rendered in place — every link, image, and code block carried over from the repo. The page below is what a contributor would see opening the project for the first time.

PLUR - Peace, Love, Unity, Roll

A momentum-based 3D soccer ball racing game built with Three.js, served through a vanilla PHP/SQLite web portal with a global leaderboard. Live at PLUR.GamingWorld.uk.


Table of Contents


Overview

PLUR is a full-stack gaming platform consisting of:

  1. A 3D browser game - A soccer ball racer inside a 3D stadium environment, built entirely in JavaScript with Three.js (WebGL). Players roll a procedurally-textured soccer ball around a stadium, collecting coins and country flags, dodging hazards, and scoring goals.

  2. A web portal - A responsive, glass-morphism-styled landing page with a leaderboard, download page, and admin panel. No frameworks - pure HTML5, CSS3, and vanilla JavaScript.

  3. A PHP/SQLite backend - REST-like API endpoints for score submission, leaderboard retrieval, asset management, and admin authentication.

  4. CI/CD pipeline - GitHub Actions workflow (placeholder) for future cross-compilation of a native C/OpenGL Windows executable.

Current version: See CHANGELOG.md for the latest release.


Live Demo

Play the game in your browser: https://PLUR.GamingWorld.uk/game/

View the leaderboard: https://PLUR.GamingWorld.uk/scores.html


Technology Stack

Frontend (Web Portal)

Technology Purpose
HTML5 Semantic markup, Web Manifest, SEO meta tags
CSS3 (vanilla) 15 modular stylesheets, glass morphism design system, CSS custom properties, clamp() responsive sizing, @import composition
JavaScript (vanilla ES6) Scroll animations (IntersectionObserver), theme toggle, parallax effects
Google Fonts Orbitron (headings), Space Grotesk (subheadings), Inter (body)
Lucide Icons 1703-symbol SVG sprite (lucide-sprite.svg)
WebP All images optimized in WebP format

Game Engine

Technology Purpose
JavaScript ES6 Modules 13-module game engine, no build step, runs natively in browser
Three.js v0.162.0 WebGL 3D rendering (loaded via importmap from unpkg CDN)
GLTFLoader + DRACOLoader Loads Draco-compressed .glb 3D stadium model
Canvas API Procedural texture generation (soccer ball pentagons, coin star emblem)
Web Audio API Sound effects (coin collect, jump, death, victory)
Touch Events API Mobile virtual joystick and jump button
requestAnimationFrame 60 FPS game loop with delta-time physics
localStorage Dev tuner settings persistence

Backend

Technology Purpose
PHP 8.1 API endpoints, session management, file uploads
SQLite 3 Database (WAL mode, foreign keys, PDO)
PDO Prepared statements for all database queries

Infrastructure

Technology Purpose
Ubuntu Linux Server OS
Apache 2.4 Web server with mod_rewrite, mod_deflate, mod_headers
Let's Encrypt SSL/TLS via certbot (auto-renewal)
Hetzner VPS/dedicated hosting
Git Version control
GitHub Repository hosting (private: saintpetejackboy/PLUR)
GitHub Actions CI/CD pipeline
just Task runner (justfile)

Build Tools (npm)

Package Purpose
@gltf-transform/core GLB/GLTF 3D model manipulation
@gltf-transform/extensions GLTF extension support
@gltf-transform/functions GLTF optimization functions
draco3dgltf Draco 3D mesh compression
gltf-pipeline GLB optimization pipeline
obj2gltf OBJ to GLTF/GLB conversion
lucide-static Icon library source
sharp Image processing and optimization

Project Structure

/var/www/plur/
├── public/                      # Apache document root
│   ├── index.html              # Landing page
│   ├── scores.html             # Leaderboard page
│   ├── download.html           # Download page
│   ├── game/                   # Game client
│   │   ├── index.html         # Game launcher (importmap, HUD, menus, dev tuner)
│   │   ├── js/                # Game JS modules (synced from game_src/js/)
│   │   └── css/
│   │       └── game.css       # Game-specific styles
│   ├── admin/                  # Admin dashboard
│   │   ├── index.html
│   │   └── admin.css
│   ├── css/                    # Portal stylesheets (15 modular files)
│   │   ├── theme.css          # Main entry point (@import chain)
│   │   ├── variables.css      # CSS custom properties
│   │   ├── reset.css          # CSS reset
│   │   ├── layout.css         # Grid/flex layout
│   │   ├── typography.css     # Font faces, sizing
│   │   ├── buttons.css        # 3D button styles
│   │   ├── cards.css          # Glass morphism cards
│   │   ├── forms.css          # Custom inputs, toggles
│   │   ├── animations.css     # Keyframe animations
│   │   ├── header.css         # Site header/nav
│   │   ├── hero.css           # Hero section
│   │   ├── footer.css         # Site footer
│   │   ├── modal.css          # Modal overlays
│   │   ├── tables.css         # Leaderboard tables
│   │   ├── scrollbars.css     # Custom scrollbar styling
│   │   ├── pages.css          # Page-specific overrides
│   │   ├── responsive.css     # Media queries
│   │   └── download.css       # Download page styles
│   ├── js/                    # Portal scripts
│   │   ├── theme-toggle.js    # Dark/light mode
│   │   ├── scroll-animations.js # IntersectionObserver fades
│   │   ├── scores.js          # Leaderboard fetching
│   │   ├── download.js        # Download verification
│   │   └── admin.js           # Admin panel logic
│   ├── assets/
│   │   ├── images/            # WebP images (~15 files)
│   │   ├── icons/             # Lucide SVG sprite
│   │   ├── models/            # 3D models
│   │   │   └── stadium.glb   # Draco-compressed stadium (8.6 MB)
│   │   └── game/             # Game-specific assets
│   ├── downloads/             # Distributable game packages
│   ├── .htaccess              # Rewrites, security headers, gzip
│   ├── robots.txt
│   ├── humans.txt
│   ├── sitemap.xml
│   └── site.webmanifest
│
├── game_src/                   # Game source code (canonical)
│   └── js/                    # 13 ES6 modules (~3,700 LOC)
│       ├── main.js            # Game loop, state machine, score submission
│       ├── ball.js            # Player ball physics, boost, procedural texture
│       ├── physics.js         # Gravity, friction, AABB collision detection
│       ├── renderer.js        # Three.js WebGL setup, lights, shadows
│       ├── camera.js          # Smooth-follow camera
│       ├── controls.js        # Keyboard + touch input, vehicle-style steering
│       ├── level.js           # Stadium GLB loading, collision proxies, goals
│       ├── collectibles.js    # Coins, flags, collection mechanics
│       ├── hazards.js         # Crushers, spikes, death zones, bumpers
│       ├── hud.js             # Score, timer, lives, speed/boost meters
│       ├── audio.js           # Web Audio API sound effects
│       ├── particles.js       # Sparkle, explosion, trail particle systems
│       └── scoreboard.js      # Leaderboard UI, on-screen keyboard
│
├── api/                       # PHP backend
│   ├── config.php             # Environment loader
│   ├── database.php           # PDO + SQLite connection
│   ├── scores.php             # GET /api/scores.php - leaderboard
│   ├── submit-score.php       # POST /api/submit-score.php - score submission
│   ├── verify-download.php    # POST /api/verify-download.php - download auth
│   ├── .htaccess              # API security rules
│   └── admin/
│       ├── auth.php           # Admin session management
│       ├── verify.php         # Session verification
│       ├── helpers.php        # Utility functions
│       ├── assets.php         # Asset upload/management
│       └── stats.php          # Game statistics
│
├── database/                  # SQLite storage
│   ├── plur.db               # Main database (not committed)
│   ├── init.php              # Schema initializer
│   └── backups/              # Timestamped backups (not committed)
│
├── docs/                     # Documentation
│   ├── quick-start.md        # Project brief and phase overview
│   ├── stack.md              # Technology overview
│   ├── post-work.md          # AI agent checklist
│   └── original-idea.md      # Original concept notes
│
├── tools/
│   └── extract-stadium-colliders.mjs  # GLB mesh collision extractor
│
├── .github/
│   └── workflows/
│       └── windows-build.yml # GitHub Actions (placeholder for C/OpenGL build)
│
├── justfile                  # Task runner commands
├── package.json              # npm dependencies
├── CHANGELOG.md              # Version history (Keep a Changelog format)
├── ROADMAP.md                # Feature roadmap
├── .env                      # Runtime config (not committed)
└── .gitignore                # Excludes secrets, databases, binaries, node_modules

Game Engine Architecture

The game engine is a custom 13-module JavaScript system with no build step. Modules communicate through ES6 imports/exports and a shared window namespace for the dev tuner.

Module Dependency Graph

main.js (entry point - game loop & state machine)
  ├── renderer.js    → Three.js scene, WebGL renderer, lights, shadows
  ├── camera.js      → Smooth-follow camera with configurable offset
  ├── controls.js    → Keyboard (WASD/Arrows/Space) + touch joystick input
  ├── ball.js        → Player sphere, physics body, procedural soccer texture
  │   └── physics.js → Gravity, drag, bounce, AABB collision detection
  ├── level.js       → Stadium GLB loader, collision proxy builder, goal zones
  ├── collectibles.js → Coin/flag placement, collection logic, scoring
  ├── hazards.js     → Crushers, spikes, bumpers, death zones
  ├── hud.js         → DOM-based HUD (score, timer, lives, speed meter)
  ├── audio.js       → Web Audio API oscillator-based sound effects
  ├── particles.js   → GPU-friendly particle systems (sparkle, explosion, trail)
  └── scoreboard.js  → End-game UI, on-screen keyboard, leaderboard display

How the Game Renders in the Browser

  1. Loading: public/game/index.html loads Three.js via an ES6 importmap pointing to the unpkg CDN. Game modules are loaded as <script type="module"> tags with cache-bust query strings.

  2. Initialization: main.js creates the Three.js scene, renderer (WebGL with PCF soft shadows, ACES filmic tone mapping), and camera. The stadium .glb model is loaded via GLTFLoader with DRACOLoader for mesh decompression.

  3. Game Loop: A requestAnimationFrame loop runs at 60 FPS with delta-time (dt) passed to all systems:

    • controls.update() - Read input state
    • ball.update(dt) - Apply forces, integrate velocity
    • physics.resolveCollisions() - AABB collision against stadium proxies
    • collectibles.update(dt) - Animate coins/flags, check collection
    • hazards.update(dt) - Animate hazards, check death collisions
    • particles.update(dt) - Update particle systems
    • camera.update(dt) - Smooth-follow the ball
    • hud.update() - Refresh DOM HUD elements
    • renderer.render() - Draw frame
  4. Rendering: Three.js renders the scene with:

    • Directional light with 4096x4096 shadow map
    • Ambient + fill lights
    • Fog for depth
    • Procedural textures (soccer ball via icosahedron math, coins via canvas radial gradients)
  5. Input: Vehicle-style steering where A/D rotate the ball's heading and W/S thrust along it. Space jumps. Mobile uses a virtual joystick (left thumb) and jump button (right thumb).

  6. Scoring: When the ball enters a goal zone, the game ends. An on-screen keyboard lets the player enter their name. The score is POSTed to /api/submit-score.php and the leaderboard refreshes.

Dev Tuner

Press \ (backslash) in-game to open the Dev Tuner panel. It provides real-time sliders for:

  • Stadium position, scale, rotation
  • Ball radius, move force, jump force, max speed
  • Physics: gravity, ground drag, air drag, bounce
  • Toggles: God Mode, Debug XYZ coordinates

Settings persist to localStorage.


Web Portal

The portal is a multi-page static site with no JavaScript framework:

Page URL Description
Landing / Hero with image-based CTAs, feature cards with parallax, glass morphism design
Game /game/ Full-screen 3D game client
Scores /scores.html Global leaderboard with sortable table
Download /download.html Game download page (password-protected)
Admin /admin/ Dashboard for asset management and statistics

Design System

  • Glass morphism: backdrop-filter: blur() with semi-transparent backgrounds
  • CSS custom properties: All colors, fonts, spacing defined in variables.css
  • Responsive: clamp() font sizing, mobile-first media queries
  • Animations: IntersectionObserver-triggered fade-ins, parallax scrolling
  • Dark/Light mode: Toggle with localStorage persistence
  • No CSS framework: All styles are hand-written, modular CSS

Backend API

All endpoints are vanilla PHP with PDO prepared statements.

Endpoint Method Description
/api/scores.php GET Fetch top 100 leaderboard entries (JSON)
/api/submit-score.php POST Submit a score {player_name, score, time_survived, flags_collected}
/api/verify-download.php POST Verify download password
/api/admin/auth.php POST Admin login (creates session token)
/api/admin/verify.php POST Verify admin session token
/api/admin/stats.php GET Game statistics (total scores, unique players)
/api/admin/assets.php POST/GET Upload and manage game assets

Database

Engine: SQLite 3 with WAL mode and foreign keys enabled.

Location: database/plur.db (not committed to git)

Schema

CREATE TABLE highscores (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  player_name TEXT NOT NULL,
  score INTEGER DEFAULT 0,
  time_survived REAL DEFAULT 0.0,
  flags_collected TEXT DEFAULT '[]',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE admin_sessions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  token TEXT UNIQUE NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  expires_at DATETIME NOT NULL
);

CREATE TABLE game_assets (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  filename TEXT NOT NULL,
  original_name TEXT,
  category TEXT,
  description TEXT,
  tags TEXT DEFAULT '[]',
  file_size INTEGER,
  mime_type TEXT,
  uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  active INTEGER DEFAULT 1
);

Initialize

just init-db
# or
php database/init.php

Backup

just db-backup
# Creates: database/backups/plur_YYYYMMDD_HHMMSS.db

Getting Started

Prerequisites

  • Ubuntu/Debian Linux (or similar)
  • Apache 2.4 with mod_rewrite, mod_deflate, mod_headers
  • PHP 8.1+ with pdo_sqlite extension
  • Node.js 18+ and npm (for build tools only)
  • just task runner (installation)
  • Git

Installation

# 1. Clone the repository
git clone [email protected]:saintpetejackboy/PLUR.git /var/www/plur
cd /var/www/plur

# 2. Install npm dependencies (build tools only, not needed for runtime)
npm install

# 3. Create environment configuration
cp .env.example .env
# Edit .env with your passwords and paths

# 4. Initialize the database
just init-db

# 5. Configure Apache VirtualHost
# Point DocumentRoot to /var/www/plur/public/
# Enable mod_rewrite

# 6. Set permissions
sudo chown -R www-data:www-data public/ database/

# 7. Set up SSL
sudo certbot --apache -d your-domain.com

# 8. Start Apache
just serve

Syncing Game Files

Game source lives in game_src/js/. After editing, sync to the public directory:

cp game_src/js/*.js public/game/js/
# Remember to update cache-bust versions (?v=0XX) in public/game/index.html

Task Runner (justfile)

All project operations use just (a modern make alternative).

just                    # List all available commands
just serve              # Restart Apache
just deploy             # Build + set permissions + restart Apache
just status             # Git status, current version, Apache status
just logs               # Tail Apache error logs (last 50 lines)
just push "message"     # Git add, commit, push to origin/main
just release 0.3.0      # Tag release, update CHANGELOG, push with tag
just bump-patch         # Auto-increment patch version (0.2.5 -> 0.2.6)
just bump-minor         # Auto-increment minor version (0.2.5 -> 0.3.0)
just db-backup          # Backup SQLite with timestamp
just init-db            # Initialize/reset database schema
just check-ssl          # Verify Let's Encrypt certificate status
just clean-old-downloads # Remove old ZIPs, keep latest
just build-game         # Placeholder (future C/OpenGL compilation)

Deployment

The project deploys via git push to a Hetzner server:

# Full deploy cycle
just deploy

# Or manually:
just build-game                              # Build (placeholder)
sudo chown -R www-data:www-data public/ database/  # Permissions
just serve                                   # Restart Apache

Release Process

# 1. Update CHANGELOG.md with changes
# 2. Run the release command
just release 0.3.0
# This will: update .env version, insert CHANGELOG header, commit, tag, push

Server Architecture

Browser ──HTTPS──▶ Apache 2.4 ──▶ public/         (static HTML/CSS/JS/assets)
                                ──▶ public/game/   (Three.js game client)
                                ──▶ api/*.php      (PHP endpoints)
                                ──▶ database/plur.db (SQLite via PDO)

Configuration

Environment Variables (.env)

DOWNLOAD_PASSWORD=<password for download page>
ADMIN_PASSWORD=<password for admin panel>
DB_PATH=/var/www/plur/database/plur.db
SITE_URL=https://PLUR.GamingWorld.uk
APP_VERSION=0.2.5

Three.js CDN (importmap)

Three.js is loaded from unpkg CDN via an importmap in public/game/index.html:

{
  "imports": {
    "three": "https://unpkg.com/[email protected]/build/three.module.js",
    "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
  }
}

No local Three.js installation is needed for runtime.

Cache Busting

All CSS and JS files use query-string cache busting (?v=0XX). Increment the version number after changes to ensure browsers load the latest files.


Security

  • HTTPS only - All HTTP redirected to HTTPS via Apache
  • Prepared statements - All database queries use PDO prepared statements
  • Input sanitization - htmlspecialchars() on all user output
  • .env excluded - Secrets never committed to git
  • .git blocked - Apache .htaccess blocks access to .git/
  • Database blocked - .db, .sql, .bak files blocked via .htaccess
  • Security headers - X-Frame-Options, X-Content-Type-Options, Content-Security-Policy
  • CORS configured - Appropriate headers on API endpoints
  • Session tokens - Admin sessions use random tokens with expiration

File Types Reference

This table covers every file type in the project, useful for AI tools and code analysis:

Extension Language/Format Count Location Purpose
.html HTML5 5 public/, public/game/, public/admin/ Web pages and game launcher
.css CSS3 ~17 public/css/, public/game/css/, public/admin/ Styling (modular, no framework)
.js JavaScript ES6 ~25 game_src/js/, public/js/, tools/ Game engine, portal scripts, build tools
.php PHP 8.1 8 api/, database/ Backend API and database
.webp WebP image ~15 public/assets/images/ Optimized images
.svg SVG 1 public/assets/icons/ Lucide icon sprite (1703 icons)
.glb glTF Binary 1 public/assets/models/ 3D stadium model (Draco-compressed)
.json JSON 3 root, game_src/ npm configs, web manifest
.md Markdown 5+ root, docs/ Documentation
.yml YAML 1 .github/workflows/ CI/CD pipeline
.xml XML 1 public/ Sitemap
.txt Plain text 2 public/ robots.txt, humans.txt
.db SQLite 3 1 database/ Application database
.mjs ES Module JS 1 tools/ Node.js build script
.ico / .png Favicon 4 public/ Browser icons

Roadmap

Completed

  • Phase 1 (v0.1.x): Infrastructure - Apache/SSL, landing page, SQLite, leaderboard API, download system, justfile, CI/CD, SEO, glass morphism design
  • Phase 2 (v0.2.x): Game Development - Three.js engine, procedural soccer ball, stadium environment, coins/flags, hazards, on-screen keyboard, leaderboard integration, touch controls, tuned physics, audio, particles

In Progress

  • Tutorial zone / guided first platform
  • Coin/flag variety and placement improvements
  • Moving platform refinements

Planned - Phase 3 (v0.3.x): Polish

  • Multiple levels/stages
  • Randomized elements for replayability
  • Power-up mechanics (double-jump, etc.)
  • Advanced hazards (wind zones, magnets, teleporters)
  • Background music / ambient audio
  • Performance optimization (LOD, culling)

Planned - Phase 4 (v1.0.0): Launch

  • Full asset integration
  • Public beta testing
  • Leaderboard seasons (weekly resets)
  • Admin panel enhancements
  • Marketing page updates
  • Cross-platform downloadable builds (see below)

Download Distribution Roadmap

The game currently runs in-browser only. The roadmap for downloadable versions:

  1. Web export (near-term): Package the browser game as a standalone HTML bundle (single-file or zip) that runs offline without a server.

  2. Electron wrapper (mid-term): Wrap the Three.js game in Electron for Windows/macOS/Linux desktop apps with:

    • Offline play
    • Native window chrome
    • Auto-updater
    • Local score caching with sync-on-connect
  3. Native C/OpenGL build (long-term): The original vision - a Neverball-inspired C/OpenGL engine cross-compiled to Windows .exe via MinGW in GitHub Actions. The CI/CD workflow (.github/workflows/windows-build.yml) is already scaffolded as a placeholder.

  4. Mobile (future): PWA-first approach (the site.webmanifest is already in place), potentially followed by Capacitor/Cordova wrapping for app store distribution.


Contributing

This is a private project by GamingWorld.uk. Contact the repository owner for contribution guidelines.


License

All rights reserved. See repository for licensing details.

Gallery

The full set.

Build something like this

Want a tool like this for your shop?

We've shipped this kind of thing before. Twenty-minute intro call, no slides.