C64 Doors

March 17, 2026 · View on GitHub

Draft — This document was generated by AI and may not have been reviewed for accuracy.

C64 Doors are WebDoors that run Commodore 64 programs inside a jsc64 emulator embedded directly in the browser. They require no additional server-side components beyond PHP — no bridge, no DOSBox, no Node.js.

Table of Contents


How It Works

Each C64 Door is a WebDoor whose index.php includes the shared _c64engine/player.php template. The engine:

  1. Authenticates the user and checks the door is enabled.
  2. Reads the game file (PRG, ROM, BIN, or D64) from the door's directory.
  3. Base64-encodes the program bytes and embeds them directly in the HTML page.
  4. Boots the jsc64 C64 emulator (Kernal, BASIC, and Character ROMs are in public_html/vendor/jsc64/js/assets/).
  5. After the 2-second BASIC boot, writes the program bytes into emulated memory and executes them.

No API calls are made at runtime — the program data travels with the page load.


Creating a C64 Door

File Structure

public_html/webdoors/
└── mygame/
    ├── webdoor.json    ← door manifest
    ├── index.php       ← sets $c64Config, includes the engine
    └── mygame.prg      ← the game (or .d64, .rom, .bin)

Copy public_html/webdoors/_c64example/ as a starting point, rename the folder to your game's slug, and replace game.prg with your actual file.

index.php

<?php
$c64Config = [
    'door_id' => 'mygame',        // must match webdoor.json "id"
    'title'   => 'My C64 Game',   // shown in the loading spinner
    'prg'     => 'mygame.prg',    // file in the same directory as index.php
];
require __DIR__ . '/../_c64engine/player.php';

That's the entire index.php. The engine resolves prg relative to the door's own directory automatically.

webdoor.json

{
    "webdoor_version": "1.0",
    "game": {
        "id": "mygame",
        "name": "My C64 Game",
        "version": "1.0",
        "author": "Author Name",
        "description": "Short description shown in the games list.",
        "entry_point": "index.php"
    },
    "requirements": {
        "min_host_version": "1.0",
        "features": []
    },
    "config": {
        "credits_cost_per_session": 0
    }
}

game.id must match door_id in index.php. The folder name is conventionally the same value.


Supported File Types

The engine detects the file type from the extension and handles it automatically:

ExtensionHandling
.prgStandard C64 PRG — reads the 2-byte load address from the file header
.p00P00 container — strips 26-byte header, then reads as PRG
.d64C64 floppy disk image — extracts PRG files; loads the first one by default
.romRaw cartridge ROM — loaded at $8000 (32768)
.binRaw binary — loaded at $8000 (32768)
otherTreated as PRG (2-byte header read)

Use load_address in $c64Config to override the default for any extension (see Configuration Reference).


Configuration Reference

All keys for $c64Config:

KeyTypeRequiredDescription
door_idstringYesDoor identifier — must match webdoor.json id
titlestringYesGame title shown in the loading spinner
prgstringOne of theseFilename of a PRG/ROM/BIN in the door's directory
d64stringOne of theseFilename of a D64 in the door's directory
prg_pathstringOne of theseAbsolute path to a PRG/ROM/BIN
d64_pathstringOne of theseAbsolute path to a D64
prg_namestringNoPRG name to auto-select from a D64 (default: first entry)
load_addressintNoOverride load address; file loaded as raw binary with no header

prg vs prg_path: Use prg for files that live next to index.php — it's the shortest form. Use prg_path only when the file lives outside the door's directory.

load_address example — loading a cartridge at a non-standard address:

$c64Config = [
    'door_id'      => 'mygame',
    'title'        => 'My Game',
    'prg'          => 'mygame.rom',
    'load_address' => 0xC000,   // override auto-detected \$8000 default
];

D64 Disk Images

A D64 door can contain a whole floppy's worth of programs. By default the engine loads the first PRG found in the disk directory. Use prg_name to select a specific one:

$c64Config = [
    'door_id'  => 'mygame',
    'title'    => 'My Game',
    'd64'      => 'mygame.d64',
    'prg_name' => 'LOADER',   // exact name as it appears in the D64 directory
];

PRG names in D64 images are uppercase — check them using the file preview in the BBS file browser, which shows the disk directory.

A .d64 file may also be passed via the prg key; the engine recognises the extension and handles it correctly.


Enabling the Door

C64 Doors are WebDoors and go through the same activation flow:

  1. Drop the door folder into public_html/webdoors/.
  2. In the BBS admin panel go to WebDoors and click Refresh.
  3. Find the new door in the list and click Enable.
  4. Adjust credits cost if desired and save.

The door will appear in the WebDoors section of the BBS for all logged-in users.