Back to Napplet Machines V8
SOURCE / PINNED RELEASE

Made of little things.

Napplet Machines V8

Release
b3f614e739f0…
Author-recorded commit
534f19acb4ba…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a

Archive hash verified: 0f7feb97e2c3d336…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,viewport-fit=cover"
    />
    <meta name="theme-color" content="#182447" />
    <title>Pocket Circuit</title>
  </head>
  <body>
    <main id="app">
      <div class="too-small">
        Needs at least 280 × 240. Enlarge this pane to drive or edit your track.
      </div>
      <div class="app-body">
        <nav class="toolbar" aria-label="Game views">
          <div class="tabs">
            <button data-view="garage" class="active">
              <svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
                <path
                  d="m4 13 2-7h12l2 7v6H4Z M5 13h14 M7 16h2 M15 16h2 M7 19v2 M17 19v2"
                /></svg
              ><span>Garage</span></button
            ><button data-view="rooms">
              <svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
                <path d="M4 8h12l-3-3 M20 16H8l3 3" />
                <circle cx="5" cy="16" r="2" />
                <circle cx="19" cy="8" r="2" /></svg
              ><span>Rooms</span></button
            ><button data-view="editor">
              <svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
                <path d="m5 19 2-6L17 3l4 4L11 17Z M14 6l4 4 M4 20h8" /></svg
              ><span>Workshop</span>
            </button>
          </div>
          <div class="race-actions" id="raceActions" hidden>
            <button id="pauseButton" title="Pause / resume · Menu button">
              Pause
            </button>
            <button id="reconnectRace" aria-label="Reconnect" hidden>
              Retry
            </button>
            <button
              id="recoverButton"
              aria-label="Recover car"
              title="Recover car · R / Y / △"
            >
              Recover ↺
            </button>
            <button id="exitRace" aria-label="Exit race">Exit</button>
          </div>
          <div class="toolbar-right">
            <span
              id="controllerState"
              title="Press the bottom face button to use a controller"
              >PRESS A / ✕</span
            >
            <span id="modeTag">LOCAL / 6 CARS</span
            ><button
              id="soundButton"
              aria-label="Enable sound"
              title="Toggle engine sound"
            >
              ♪ Off
            </button>
          </div>
        </nav>
        <div class="workspace">
          <aside id="panel">
            <section id="garagePanel" class="panel-content">
              <div class="section-line">
                <span>YOUR RIDE</span><span class="muted">01 — 04</span>
              </div>
              <div id="carPicker" class="car-picker"></div>
              <div id="carDetail"></div>
              <div class="section-line">
                <span>CIRCUIT</span><span class="dot">●</span>
              </div>
              <label class="sr-only" for="trackSelect">Circuit</label
              ><select id="trackSelect"></select>
              <div class="track-facts">
                <span id="trackLength"></span><span>3 LAPS</span
                ><span id="wallFact"></span>
              </div>
              <label class="field horizontal"
                >Grid size<select id="gridSize">
                  <option value="6">6 cars</option>
                  <option value="4">4 cars</option>
                  <option value="2">2 cars</option>
                </select></label
              >
              <button id="raceButton" class="primary big">
                Race with AI <span>↗</span>
              </button>
              <p class="hint">Trade paint. Catch air. Stay on the road.</p>
              <div class="control-guide">
                <div><kbd>W A S D</kbd><span>or arrow keys to drive</span></div>
                <div>
                  <kbd>SPACE</kbd><span>handbrake · release to grip</span>
                </div>
                <div><kbd>S / ↓</kbd><span>brake / reverse</span></div>
                <div><kbd>R</kbd><span>recover your car</span></div>
                <div>
                  <kbd>RT / LT</kbd><span>gas / brake · stick to steer</span>
                </div>
                <div><kbd>B / ○</kbd><span>drift · Menu to pause</span></div>
                <div>
                  <kbd>LB / RB</kbd><span>choose car · D-pad ↑↓ track</span>
                </div>
              </div>
            </section>
            <section id="roomsPanel" class="panel-content" hidden>
              <div class="section-line">
                <span>MULTIPLAYER</span
                ><span id="networkBadge">ONLINE ROOMS</span>
              </div>
              <label class="field"
                >Driver name<input
                  id="playerName"
                  maxlength="18"
                  value="Racer"
                  autocomplete="off"
              /></label>
              <div id="browseRooms">
                <label class="field"
                  >Room name<input
                    id="roomName"
                    maxlength="36"
                    value="Tiny cars, big trouble" /></label
                ><label class="field horizontal"
                  >Player limit<select id="roomCapacity">
                    <option value="6">6 players</option>
                    <option value="4">4 players</option>
                  </select></label
                ><button id="hostButton" class="primary">
                  Host game <span>+</span>
                </button>
                <div class="section-line">
                  <span>OPEN ROOMS</span
                  ><button id="refreshRooms" class="text-button">
                    Refresh ↻
                  </button>
                </div>
                <div id="roomList" class="room-list">
                  <p class="hint">Refresh to find a race.</p>
                </div>
                <label class="field"
                  >Or join by room code<input
                    id="roomCode"
                    placeholder="Paste room code"
                    maxlength="160" /></label
                ><button id="joinCode">Join room</button>
              </div>
              <div id="activeRoom" hidden>
                <div id="roomTitle" class="room-title"></div>
                <input
                  id="activeCode"
                  readonly
                  aria-label="Room code"
                  data-napplet-select="all"
                />
                <p id="connectionState" class="hint">Connecting…</p>
                <div id="members"></div>
                <button id="readyButton">Ready up</button
                ><button id="startRoom" class="primary">Start race</button
                ><button id="leaveRoom" class="subtle">Leave room</button>
                <p class="hint">
                  Empty seats become AI. Choose your car in the garage before
                  joining. The host’s circuit is used for everyone.
                </p>
              </div>
            </section>
            <section id="editorPanel" class="panel-content" hidden>
              <div class="section-line">
                <span>TRACK WORKSHOP</span><span class="dot">● LIVE 3D</span>
              </div>
              <label class="field"
                >Track name<input id="trackName" maxlength="48"
              /></label>
              <div class="segmented">
                <button id="moveTool" class="active">↖ Edit points</button
                ><button id="drawTool">✎ Draw loop</button>
              </div>
              <div class="segmented">
                <button id="addPoint" aria-pressed="false">+ Add point</button
                ><button id="wallTool" aria-pressed="false">▰ Sections</button>
              </div>
              <p id="editorHint" class="hint">
                Drag a point in the plan. The curve closes automatically.
              </p>
              <p id="curveNotice" class="hint" hidden></p>
              <div class="section-line">
                <span id="pointLabel">POINT 01</span
                ><span id="pointCount"></span>
              </div>
              <label class="field"
                >Elevation <output id="heightValue"></output
                ><input
                  id="heightInput"
                  type="range"
                  min="2"
                  max="24"
                  step=".5"
              /></label>
              <label class="field"
                >Point width <output id="widthValue"></output
                ><input id="widthInput" type="range" min="5" max="20.8" step=".05"
              /></label>
              <button id="removePoint">− Remove point</button>
              <div class="section-line">
                <span id="sectionLabel">SECTION 1 → 2</span>
              </div>
              <label class="check"
                ><input id="segmentWall" type="checkbox" /> Walls on this
                section</label
              >
              <label class="field"
                >Wall thickness <output id="wallThicknessValue"></output
                ><input
                  id="wallThicknessInput"
                  type="range"
                  min=".3"
                  max="2"
                  step=".1"
              /></label>
              <div class="button-row">
                <button id="allWalls">All walls</button
                ><button id="noWalls">No walls</button>
              </div>
              <div class="section-line">
                <span>DRAFT</span><span id="saveState">UNSAVED</span>
              </div>
              <div class="button-row">
                <button id="saveTrack">Save track</button
                ><button id="loadTrack">Load saved</button>
              </div>
              <button id="testTrack" class="primary">
                Test drive <span>↗</span>
              </button>
              <p class="hint">
                Z is height. Higher crests launch cars; open edges let them
                fall. Up to 32 points in a 260 × 260 m area.
              </p>
            </section>
          </aside>
          <div class="stage" id="stage">
            <div class="scene-top" id="sceneInfo">
              <div>
                <span class="scene-kicker">NEXT UP / 3 LAP DASH</span>
                <h2 id="sceneTitle">Pinefall Circuit</h2>
                <span id="sceneSubtitle"
                  >Ridgelines, switchbacks & a little bad luck.</span
                >
              </div>
              <div class="scene-badge">03<span>LAPS</span></div>
            </div>
            <div class="views">
              <div id="planWrap" hidden>
                <div class="plan-tools">
                  <button id="fitPlan">Fit track</button
                  ><button id="fullPlan">Full area</button>
                </div>
                <canvas id="plan" aria-label="Track plan editor"></canvas
                ><span class="canvas-label" id="planMode"
                  >PLAN / DRAG POINTS</span
                >
              </div>
              <div class="render-wrap">
                <canvas
                  id="scene"
                  tabindex="0"
                  aria-label="3D racing circuit"
                ></canvas>
                <div id="viewTools">
                  <button id="orbitLeft" aria-label="Rotate view left">↶</button
                  ><button id="orbitRight" aria-label="Rotate view right">
                    ↷</button
                  ><button id="zoomIn" aria-label="Zoom in">+</button
                  ><button id="zoomOut" aria-label="Zoom out">−</button
                  ><button id="cameraButton">Follow</button>
                </div>
              </div>
            </div>
            <div id="raceHud" hidden>
              <div class="hud-primary">
                <strong id="position">1<span>/6</span></strong>
                <div>
                  <span id="lap">LAP 1 / 3</span><b id="raceTime">00:00.0</b>
                </div>
              </div>
              <ol id="raceOrder"></ol>
              <div class="speed">
                <em id="driftState" hidden>DRIFT</em><b id="speed">0</b
                ><span>KM/H</span>
              </div>
            </div>
            <div id="countdown" hidden aria-live="polite"></div>
            <div id="touchControls" hidden>
              <div
                id="steeringPad"
                role="slider"
                tabindex="0"
                aria-label="Analog steering"
                aria-valuemin="-100"
                aria-valuemax="100"
                aria-valuenow="0"
                aria-valuetext="Centred"
              >
                <span class="control-caption">STEER</span>
                <span class="steering-rail" aria-hidden="true"><i></i></span>
                <span class="steering-thumb" aria-hidden="true">↔</span>
              </div>
              <div
                id="pedalPad"
                role="group"
                aria-label="Slide between gas, brake and drift"
              >
                <button
                  data-drive="go"
                  aria-label="Accelerate"
                  aria-pressed="false"
                >
                  <b>▲</b> GAS
                </button>
                <button
                  data-drive="stop"
                  aria-label="Brake and reverse"
                  aria-pressed="false"
                >
                  BRAKE
                </button>
                <button
                  data-drive="drift"
                  aria-label="Accelerate and drift"
                  aria-pressed="false"
                >
                  ≋ DRIFT
                </button>
              </div>
            </div>
            <div class="scene-bottom" id="sceneBottom">
              <span><i></i> BUMP · JUMP · REPEAT</span
              ><span>Drag the view to orbit ↔</span>
            </div>
            <div id="results" hidden>
              <div class="result-sheet">
                <span class="section-line">CHEQUERED FLAG</span>
                <h2 id="resultTitle">Race finished</h2>
                <ol id="resultOrder"></ol>
                <button id="againButton" class="primary">
                  Back to garage ↗
                </button>
              </div>
            </div>
          </div>
        </div>
        <output id="status" aria-live="polite">Ready to roll.</output>
      </div>
    </main>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>