Welcome to the repository for my personal website project, currently showcasing an advanced implementation of Conway's Game of Life. This project serves as both a learning exercise and the foundation for future website features, now including deployment configurations and basic monitoring.
๐ You can see it live at http://valis-world.com! ๐
This implementation of Conway's Game of Life runs the core simulation logic within a Web Worker for enhanced performance, allowing for very large grid sizes. The rendering is done using HTML5 Canvas, and the user interface supports smooth panning, zooming, boundary clamping, and interactive pattern spawning. The project is configured for deployment using Nginx and includes integration for monitoring with Uptime Kuma.
- Conway's Game of Life Simulation: Core rules implemented accurately.
- Large Configurable Grid: Currently set to 8000x2800 logical pixels (1600x560 cells).
- Toroidal Grid: Edges wrap around for continuous simulation space.
- Performance Optimizations:
- Web Worker: Simulation logic runs in a background thread, keeping the UI responsive.
- Sparse Worker Implementation: Efficiently handles states with fewer live cells using a Set.
- Canvas Rendering: Direct pixel manipulation for fast drawing.
- Optimized Drawing: Only renders cells currently within the viewport.
- Smooth Interactions:
- Panning: Click and drag (left mouse button) to move the view.
- Zooming: Mouse scroll wheel zooms in/out, centered on the cursor position.
- Smooth Animation: Uses
requestAnimationFramefor fluid zoom transitions.
- Boundary Clamping: Prevents zooming out further than fitting the entire grid and stops panning past the grid edges.
- Interactive Pattern Spawning:
- Right-click places and SPACE cycles through a predefined list of patterns (
glider,lwss,mwss,acorn,rPentomino,hwss,gosperGliderGun,pulsar). - Holding the right mouse button "spams" the currently selected pattern at the cursor location.
- Right-click places and SPACE cycles through a predefined list of patterns (
- Dense Initial State: Includes a wide variety of oscillators, spaceships, methuselahs, guns, and complex structures for an active start.
- Monitoring: Integrated with Uptime Kuma for basic service availability monitoring.
- HTML5
- CSS3
- JavaScript (ES6+)
- Web Workers API
- HTML5 Canvas API
- Deployment: Nginx (configuration included in
nginx-config/) on Raspberry Pi - Monitoring: Uptime Kuma
The key to this application's performance is the strict separation of concerns between two main JavaScript files, which run in different browser threads. This architecture allows for a massive, computationally intensive simulation to run smoothly without ever freezing the user interface.
script.js(The Main/UI Thread): This is the "Control & Render" thread. It is responsible for everything the user sees and interacts with: drawing the grid, handling mouse and keyboard input, and managing the overall application state.worker.js(The Background/Worker Thread): This is the dedicated "Simulation Engine." Its sole purpose is to run the Game of Life simulation logic, freeing the main thread from heavy calculations.
This file is organized into several classes, each with a specific responsibility, following Object-Oriented principles.
This is the central orchestrator or "brain" of the application.
- Role: It creates instances of all the other manager and handler classes and "wires" them together so they can communicate.
- Key Action: Its
initialize()method kicks off the entire application, setting up the renderer, view, input handlers, and starting the Web Worker.
This class acts as the "camera" for the simulation.
- Role: It manages the state of the viewport, including the current zoom level and pan (translation) offset.
- Core Concept: It uses a
targetZoomLevelandtargetTranslateto create smooth animations. Instead of instantly changing the view, it sets a target and usesrequestAnimationFrameto interpolate the current view towards the target, frame by frame. - Key Feature: The
clampView()method prevents the user from panning or zooming too far, ensuring the grid is always accessible.
This is the bridge between the user and the application.
- Role: It centralizes all event listeners (
mousedown,mousemove,wheel,keydown). - Function: It translates raw user input into meaningful commands, such as calling
viewManager.setTargetView()when the user drags the mouse, or telling theGameControllerto place a new pattern on a right-click.
This class is the "librarian" for all the Game of Life patterns.
- Role: It loads, stores, and provides the blueprints for patterns like "glider" or "pulsar".
- Implementation: It contains a small set of hardcoded patterns for quick access and uses an
asyncmethod withfetchandPromise.allto load larger, more complex patterns from.jsonfiles at startup.
This is a textbook example of the Strategy Pattern.
BaseRenderer(The Abstract Blueprint): This class contains all the common rendering logic.- Viewport Culling: Its most important optimization. Instead of trying to draw all 22+ million potential cells, it calculates the small rectangular portion of the grid currently visible in the viewport and only iterates over those cells.
- High-DPI Support: It uses
window.devicePixelRatioto ensure the rendering is crisp on high-resolution displays.
RectRenderer&CircleRenderer(The Concrete Strategies): These classes inherit fromBaseRenderer. Their only job is to provide a specific implementation for thedrawCells()methodโone draws usingfillRect(), the other usingarc(). TheGameControllercan swap between these "strategies" at any time.
A simple but vital utility class.
- Role: It acts as a single source of truth for all static configuration values (e.g.,
CELL_SIZE,GAME_SPEED). This prevents "magic numbers" and makes the application easy to tweak.
This script is the computational powerhouse. It runs in complete isolation and communicates with script.js only through messages.
This is the most critical optimization in the entire project. A naive approach would use a massive 2D array to store the state of every cell. This would consume a huge amount of memory and be incredibly slow to process.
- The Solution: Instead, the worker only keeps track of the cells that are alive. It does this using a JavaScript
Set, which provides highly efficientadd,delete, andhasoperations. A cell's coordinate(x, y)is stored as a simple string key:"x,y".
This is the engine's core logic, designed to work perfectly with the sparse set.
- Find Relevant Cells: The algorithm doesn't check every cell on the grid. It knows that the only cells that can possibly change state are the currently live cells and their immediate neighbors. It iterates through the
liveCellsset and builds a temporary list of these "relevant cells". - Count Neighbors: As it identifies relevant cells, it keeps a count of how many live neighbors each one has in a
Map. - Apply Rules: Finally, it iterates only over the small set of relevant cells and applies the four rules of Conway's Game of Life based on the neighbor count.
When the worker finishes a new generation, it doesn't send the entire new state of all live cells back to the main thread. Instead, it sends a small array containing only the cells that changed state (a "delta"). This dramatically reduces the amount of data that needs to be passed between threads, which is another key performance factor.
Together, these two scripts and their internal architectures form a robust system that delegates tasks efficiently, allowing a complex and resource-intensive simulation to run beautifully in a web browser.
Because this project uses Web Workers and the web assets are located in the dist/ directory, you cannot simply open dist/index.html directly in your browser using a file:/// URL due to browser security restrictions (null origin). You need to serve the files using a local web server from the dist directory.
Option 1: Python
- Navigate to the project's root directory in your terminal.
- Run:
cd dist && python -m http.server 8000(orpython3 -m http.serverorpython -m SimpleHTTPServerfor Python 2). - Open your browser to
http://localhost:8000. - Press
Ctrl+Cin the terminal to stop the server. Then runcd ..to get back to the project root.
Option 2: Node.js (http-server)
- Install:
npm install -g http-server(if you haven't already). - Navigate to the project's root directory in your terminal.
- Run:
http-server dist -p 8000 -o(This tells it to serve thedistfolder). - This will usually open the browser automatically to the correct address.
Option 3: VS Code Live Server
- Install the "Live Server" extension in VS Code.
- Open the project folder in VS Code.
- Right-click
dist/index.htmlin the VS Code explorer and choose "Open with Live Server".
This project is currently deployed and running at http://valis-world.com, served by Nginx on a Raspberry Pi.
Nginx Configuration:
The nginx-config/ directory in this repository contains the configuration files intended for the Nginx deployment:
nginx.conf: A base/global Nginx configuration template.valis-world.conf: The site-specific configuration (serverblock) for this project.
General Deployment Steps:
- Transfer Web Files: Copy the contents of the local
dist/directory to the designated web root directory on the Raspberry Pi server (e.g.,/var/www/valis-world/distor similar). - Transfer & Configure Nginx:
- Copy the configuration files from
nginx-config/to the appropriate locations on the server (e.g.,/etc/nginx/nginx.confand/etc/nginx/conf.d/valis-world.confor using thesites-available/sites-enabledstructure). - Important: Modify the
valis-world.conffile on the server to set the correctserver_name(your domain) androotpath (pointing to where you copied thedistfiles in step 1). - Test the configuration:
sudo nginx -t - Reload Nginx to apply changes:
sudo systemctl reload nginx
- Copy the configuration files from
- Configure Uptime Kuma: Set up your Uptime Kuma instance (if running separately) to monitor the live URL (
http://valis-world.com) to check for availability.
(Refer to the README.md within the nginx-config/ directory for potentially more specific deployment notes, if you added one there).
As this is the base for my personal website, future plans include:
- Adding more interactive elements or simulations.
- Integrating content sections (blog, portfolio, about).
- Refining the UI/UX.
- Further performance tuning if needed.
- Implementing HTTPS/SSL.
- Creating a public status page via Uptime Kuma.
This is primarily a personal project, but suggestions or bug reports via GitHub Issues are welcome!