Skip to content
This repository was archived by the owner on Oct 31, 2026. It is now read-only.
dockfriesPublic archive

About

Fully static GTA San Andreas race-route map builder.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Track Layout · Race Route Studio

English · 简体中文

Track Layout is a fully static GTA San Andreas race-route map builder. Give it a track JSON that only contains checkpoints; it snaps each CP to the game's road graph, routes between them with A*, renders a customizable route map, and exports PNG / SVG.

Pure vibe coding: architecture, code, debugging, docs and samples were iterated by AI in conversation. It works, but review and test it before production use.

Track Layout dark UI

Inspired by and built on data from neufox's Node Viewer and neufox's Rec Editor: plain HTML/CSS/JS + Leaflet CRS.Simple + samap.png, with no build step and no application backend (server.js is only a local static file server).

Features

  • Track JSON import: choose a file or drop it in; tolerant parsing for arrays, checkpoints / cps / points, [x,y,z], pos / position, and similar shapes.
  • Automatic road routing: the default Auto mode prefers a full A* route over SA vehicle nodes. If some CPs cannot snap, it still keeps as much of the road network as possible, fills gaps with straight lines, and explains this under Route. You can also force Road / Spline / Straight.
  • Closed loops: when Closed loop is enabled, the last point returns to the start and the first point becomes both start and finish.
  • CP editing: drag on the map, edit coordinates, move up/down, delete, or add CPs by clicking the map. Show or hide checkpoints in both the map and the export; the sidebar list is collapsible and virtualized for tracks with hundreds of CPs.
  • CP display filters: show all markers, start + finish only, start only, finish only, or CP style (all points, including start / finish, rendered as ordinary checkpoints without S/F colours or badges). Marker density can be automatic (about one marker per 600 m, capped at 72), or manual every 2nd / 5th / 10th / 20th. Display thinning never changes routing or CP indices, and the sidebar editing list always keeps every checkpoint.
  • Large warning sets: the notes/warnings panel is collapsible too, and both warning lists virtualize their rows so large tracks stay responsive.
  • Visual customization: 9 theme presets; solid / along-route / linear / elevation / rainbow route gradients; outline, glow, dashes, direction arrows; CP shape, size, colors, numbering and labels; base-map brightness / contrast / saturation / hue / invert / blur / tint / grid; title and legend.
  • Custom theme state: any visual override switches the active theme to Custom while remembering the base preset. Changing it back exactly restores the preset automatically; Restore preset resets visuals to that base without losing title text, visibility toggles, or a custom base map.
  • PNG export: 16:9, 4:3, 1:1, 9:16, 4K, transparent background, and full track / current view framing.
  • SVG export: route, CPs, title and legend stay vector; the base map is embedded as a raster image, or turn it off for a pure-vector route map.
  • Custom base maps: upload an image and set its coordinate bounds for other games or hand-made maps.
  • Sample library: the Samples menu has fuzzy search, keyboard navigation and a virtualized scrolling list, so forks can add hundreds of sample tracks without slowing the UI; the sidebar shows the first few plus an "all samples" shortcut.
  • Autosave: track, styles and export settings live in this browser's localStorage and survive reloads.
  • Stacked toasts: rapid actions show up to five top-center notices at once; extra notices queue and dismiss oldest-first.
  • Shortcut reference: press ? or click the keyboard icon to open a grouped keyboard-shortcut dialog. Single-key shortcuts can be disabled there; modifier shortcuts and Escape keep working.
  • Slider helpers: double-click a range input to restore its current theme/default value; click the numeric readout to type an exact value with min/max validation.
  • Safe CP editing: map marker dragging is locked by default to prevent accidental moves; press L or use the lock button to unlock, and undo/redo checkpoint edits (drag, coordinate input, add/delete/reorder).
  • Responsive map: window resizing is debounced (180 ms), then the map re-fits the current track; virtual lists and the scale bar refresh at the same time.
  • Route playback: play a direction arrow from start to finish with a 0.1×–4× speed slider (click the × value to type an exact speed); a floating HUD shows playing/paused/finished status, a draggable progress bar, and stop controls. Optional map following keeps the arrow centered; an additional heading-up mode rotates the map around the playback point so the arrow always points up (dragging or zooming turns both modes off). The animation is map-preview only and never appears in exports.
  • Collapsible route notes: route warnings can be folded away and use the same virtualized list rendering for long runs.
  • Clear local cache: the sidebar footer has a cache reset action that removes this app's localStorage and Cache Storage data, then reloads.
  • Responsive workspace: below 980px the sidebar becomes a bottom sheet with peek / half / full snap points. Drag the handle, tap it, use the chevron, or use ↑ / ↓ / Home / End while the handle is focused. On desktop the sidebar can be collapsed from the top bar.
  • Compact action menu: below 720px the top-bar actions collapse into a hamburger panel containing file, sample, language, theme, help and shortcut controls. It closes on outside click, selection, or Esc.
  • Movable help dialogs: drag the title bar of the JSON-format or keyboard-shortcut dialogs to reposition them. Positions are clamped to the viewport, reset when reopened, and double-clicking the title bar recenters.
  • Small-screen controls: below 480px the section tabs show icons only. Checkpoint, track-note and route-note headers are full-width collapse toggles, not just the chevron.
  • Toast feedback: Ant-Design-style elevated notices with success/error/info icons, soft shadows, top-center stacking and slide-fade motion.
  • Title / legend placement: independently anchor both overlays to any of six positions (top/middle/bottom × left/center/right), then fine-tune horizontal and vertical offsets as percentages of the canvas. The same placement is used by the live map, PNG, SVG, mini preview and fullscreen preview.
  • Live export preview: the thumbnail in the Export pane re-renders after style, route, or size changes and shows a loading spinner while the next frame is being prepared.
  • Export watermark: the bottom-right GTA: SAN ANDREAS · TRACK LAYOUT credit can be enabled or disabled in the Export pane.
  • Fullscreen preview: click the preview image to open a full-viewport lightbox with wheel/trackpad zoom, pinch zoom on touch screens, drag-to-pan, double-click reset, and an optional native Fullscreen API button. Keyboard: +/- zoom, arrows pan, 0 reset, Esc close.
  • Source link: the sidebar footer GitHub icon opens the source repository.
  • Internationalization: first visit follows the browser locale (zh* → Simplified Chinese, everything else → English); the globe menu in the top bar overrides it. UI, map title/legend, CP list, notices, help and PNG/SVG export text all follow the active language.
  • Dark / light UI: the sun / moon / monitor menu in the top bar switches Light / Dark / System. System follows prefers-color-scheme live, and the choice is remembered in this browser. This only affects the app chrome, not the route-map artwork itself.

Quick Start

Requires Node.js 18+. The app is static: no build step and no npm install required.

git clone https://github.com/dockfries/track-layout
cd track-layout

# Option A (recommended): temporary static server via npx
npx --yes serve . -l 8173

# Option B: the zero-dependency static server in this repo
node server.js 8173
# or: npm start

Open http://127.0.0.1:8173/. The first visit automatically loads the Los Santos Loop sample.

  • npx serve downloads serve temporarily on first use; it does not write into this repo.
  • npm run serve is equivalent to Option A; npm start is equivalent to Option B.
  • Both options only serve static files. No track data is uploaded.
  • If port 8173 is taken, serve may choose another port; use the URL printed in the terminal.
  • Open the app over HTTP. It needs fetch('data/nodes.json'); double-clicking index.html will be blocked by the browser's file:// restrictions.

Tests:

npm test
# or: node tests/run.js

Language / Internationalization

  • Default language: the first visit reads navigator.languages. Chinese locales such as zh, zh-CN and zh-Hans get Simplified Chinese; every other locale gets English.
  • Switch and remember: use the globe menu in the top bar. The choice is stored in localStorage under track-layout:lang.
  • Coverage: static UI, tabs, help dialog, map title/legend, CP list, route notices, toasts, sample names, theme names, and PNG/SVG title, legend and CP labels all follow the active language.
  • Adding strings: UI copy uses semantic English keys in the MESSAGES table in js/i18n.js (key: { en, zh }); legacy Chinese text in static HTML is mapped through STATIC_TEXT, and rich help HTML lives in RICH. Add entries when you add UI copy.
  • html.i18n-pending hides the UI until a language is applied, so English users do not see a flash of Chinese text.

Appearance

Light UI

  • Use the System / Dark / Light picker in the top bar to switch the workbench appearance.
  • System follows prefers-color-scheme and updates live when the OS changes.
  • Manual choices are stored in localStorage under track-layout:theme and survive reloads; choose System to follow the OS again.
  • Appearance only affects the workbench UI (sidebar, dialogs, map controls, etc.). The map title, legend, CP markers and export artwork are still controlled by the themes under Style.

Track JSON Format

The simplest form is an ordered CP array; array order is track order:

{
  "name": "Los Santos Loop",
  "author": "your name",
  "closed": false,
  "checkpoints": [
    { "x": 2287.4, "y": -1605.9, "z": 2.6 },
    { "x": 1617.6, "y": -1205.8, "z": 51.8 },
    { "x": 1599.0,  "y": -2378.2, "z": 12.4 }
  ]
}
Field Required Description
checkpoints yes* Checkpoint array. Also accepts cps / points / cp / waypoints.
name no Track name; falls back to the file name.
author no Author.
description no Description; kept only in the JSON.
closed no Whether the track is a closed loop; default false.
map no Map name; default san-andreas.
checkpoints[].x / .y yes World coordinates. x is east, y is north.
checkpoints[].z no Elevation; used for elevation gradients and height-aware snapping.
checkpoints[].label no Display label.
checkpoints[].radius no Reserved field, preserved as-is.

* A top-level array is also accepted: [{ "x": 0, "y": 0 }, ...].

Accepted variants:

[
  [100, 200, 10],
  { "pos": [300, 400, 12] },
  { "position": { "x": 500, "y": 600, "z": 8 } },
  { "x": 700, "y": 800, "type": "finish", "label": "Finish" }
]

Roles come from order, not type: for an open track the first point is Start and the last is Finish; for a closed loop the first point is Start / Finish. type is parsed, but the final role is always determined by order.

CP numbering: Start / Finish use S, F and S/F badges; ordinary checkpoints use their array index. The sidebar, map markers and route notices all share this numbering.

See track.schema.json for the full JSON Schema and samples/ for examples.

Routing

  • The road graph comes from data/nodes.json (extracted from NODES0.DAT–NODES63.DAT: 30,587 vehicle nodes and about 63k directed connections).

  • Each CP looks for a vehicle node within the snap radius (80 m by default); when z is available, height difference adds a penalty. Snapping prefers the main road component when every CP can join it, otherwise it stays on the nearest local component so short island routes are not detoured.

  • Adjacent CPs are connected with A*. Edge cost = 2D length × average of the two node weights:

    Node type Weight
    Highway 0.8
    Normal road 1.0
    Parking 1.3
    Emergency lane 2.6
    Water path 12
  • Auto first tries a full road-network A* route. If some CPs cannot snap or some segments are unreachable, it keeps the reachable road parts, fills gaps with straight lines, and shows partial road plus the specific reasons under Route. If no leg at all can be routed, it uses straight connections. Only when the road graph is unavailable does it fall back to a Catmull–Rom spline.

  • Road mode routes segment by segment, fills failed segments with straight lines, and lists the reasons under Route.

  • Road routes are simplified with Douglas–Peucker and 0–2 Chaikin corner-cutting passes for a cleaner race line; the smoothing slider controls the strength.

  • Closed loops additionally route the last point back to the start.

Limitation: a custom base map only changes the display, not the coordinate system or the road graph. Routing still uses GTA SA vehicle nodes. Use Spline or Straight for air, water or hand-made maps.

Visual Styling

  • Theme presets: Neon Night, Tactical Radar, Paper Map, Miami Sunset, Blueprint, Mono, Cyberpunk 2077, White Chocolate, Rainbow Flow.
  • Route gradients:
    • Solid
    • Along route: start color → finish color over route length
    • Linear: gradient in canvas direction
    • Elevation: gradient by CP / route z
    • Rainbow: continuous full-spectrum light trail, Tesla style
  • Route layer: outline, glow, dashes, direction arrows (spacing / size / color). Automatic arrow spacing targets ~48 arrows with a 140 m minimum, so full-map routes stay readable.
  • Checkpoints: show / hide, display range, marker density, CP-style endpoints, uniform start/finish colours, circle / square / diamond, size, fill, border, numbering, labels, halo, start color, finish color.
  • Base map: brightness, contrast, saturation, hue, invert, blur, tint strength, grid (8 × 8 on the standard SA bounds), optional grid numbers (0 at the top-left, row-major), background color, visibility; upload a custom image and set minX/maxX/minY/maxY. Custom images and their bounds are not written to localStorage (too large); re-select the image and bounds after reload.
  • Title & legend: show switches, eyebrow, title, subtitle, text/accent/background colors, background opacity, overall scale, title font size, subtitle font size, legend background.

Can visual styles be saved to JSON? Currently they are autosaved in localStorage only; Save JSON contains track data, not visual styles.

Export

Preview export renders a fresh PNG at the current output size in a dialog; Download PNG and Download SVG use the same renderer.

  • PNG: raster output, transparent background, full track / current map view, 16:9 / 4:3 / 1:1 / 9:16 / 4K or custom sizes (up to 4096).
  • SVG: route, CPs, arrows, title and legend stay vector; the base map is embedded as a cropped raster with current filters/tint. Turn off Include base map for a pure-vector route map.
  • Exports crop to the track bounds with padding; near map edges the viewport is pushed back inside the map where possible to avoid blank strips.
  • Title scale, title/subtitle font sizes, theme colors and map filters are re-rendered for export, not screenshotted.
  • Very long titles and subtitles are ellipsized to fit the export instead of running past the canvas edge.

Keyboard Shortcuts

Key Action
F Fit track
G Show / hide grid
M Show / hide base map
C Show / hide checkpoints
L Lock / unlock map CP dragging
Ctrl/Cmd + Z Undo last checkpoint edit
Ctrl/Cmd + Shift + Z Redo checkpoint edit
+ / = Enter / leave map add-CP mode
Esc Cancel add mode / clear selection / close dialog
Delete Delete the selected checkpoint
Ctrl/Cmd + O Open JSON
? Open the keyboard shortcuts panel

Project Structure

README.md               English documentation
README.zh-CN.md         Simplified Chinese documentation
LICENSE                 MIT license
package.json            start / test scripts
.gitignore              ignores for local caches, debugging and editor files
index.html              page shell
style.css               workbench styling
js/track.js             JSON parsing, road graph, A*, smoothing, geometry
js/style.js             style model, themes, colors, marker SVG
js/i18n.js              semantic i18n messages + static-text fallback + language switch
js/mapview.js           Leaflet map rendering and CP interaction
js/export.js            PNG / SVG renderer
js/app.js               app state and UI wiring
data/nodes.json         SA vehicle road graph (required, commit it)
assets/samap.png        SA base map (required, commit it)
assets/icons/           Lucide / Iconify sprite and license
samples/                sample tracks + index.json (commit them)
docs/preview.webp        dark README screenshot
docs/preview-light.webp  light README screenshot
vendor/leaflet/         Leaflet 1.9.4 (commit it)
tools/make-samples.js   regenerate samples from the road graph
tests/run.js            parser / geometry / routing tests
server.js               local static server
track.schema.json       track JSON Schema

Git Ignore Rules

.gitignore only excludes local artifacts such as node_modules/, logs, build caches, .env, editor/OS files, temporary backups, and headless-browser artifacts such as tools/debug/, tools/edge-*/ and tools/*-profile/.

data/nodes.json, assets/samap.png, assets/icons/, docs/preview.webp, docs/preview-light.webp, samples/*.json and vendor/leaflet/ are required runtime or documentation assets. Keep them in the repository; do not delete them or add them to the ignore list.

Regenerating Samples

Sample CPs are real road-graph paths built from hand-picked waypoints (each waypoint plus each segment midpoint), so re-routing cannot cut corners:

npm run samples

Credits

Pure vibe coding: architecture, code, debugging, docs and samples were iterated by AI in conversation; there is no hand-written line-by-line implementation. Review and test before use.

Reference Projects

  • neufox's Node Viewer: provided samap.png, data/nodes.json (extracted from NODES0.DAT–NODES63.DAT), the Leaflet CRS.Simple map presentation, and the vehicle-node flags / adjacency structure. This project extends those data and ideas with CP snapping, A* routing and route rendering.
  • neufox's Rec Editor: provided the static, no-build page organization and interaction ideas around path smoothing / resampling / drag editing. The CP editor, map interaction and export flow here reference it.

Data and Third-Party Assets

  • samap.png and data/nodes.json come from neufox-node-viewer; the road graph was extracted from GTA San Andreas NODES0.DAT–NODES63.DAT.
  • Map engine: Leaflet, BSD-2-Clause, vendored under vendor/leaflet/.
  • UI icons: Lucide, fetched through Iconify and bundled in assets/icons/sprite.svg. Lucide uses the ISC license; see assets/icons/LICENSE.
  • GTA: San Andreas and its map/road data are owned by Rockstar Games. This is an unofficial fan tool and is not affiliated with Rockstar.

License

Source code, documentation and configuration generated by this project are released under the MIT License; see LICENSE.

MIT covers this repository's source, docs and generated configuration only. The following retain their original rights:

  • assets/samap.png, data/nodes.json: from neufox-node-viewer / GTA: San Andreas; rights belong to Rockstar Games and the original project authors.
  • docs/preview.webp, docs/preview-light.webp: screenshots containing GTA: San Andreas map imagery; not covered by MIT.
  • assets/icons/: Lucide icons, ISC license; see assets/icons/LICENSE.
  • vendor/leaflet/: Leaflet, BSD-2-Clause; see vendor/leaflet/LICENSE.
  • GTA coordinate data in sample tracks is subject to the original game-data rights as well.

The GTA map and road data are included for this unofficial fan project with attribution. No separate redistribution license is granted for them; confirm that you have the right to redistribute them before publishing a fork or using them commercially.

Privacy

Track JSON, dropped images and style settings are processed entirely in the browser. There is no upload endpoint and no analytics. The only external requests are for local data/nodes.json, samples/*.json and base-map files.

About

Fully static GTA San Andreas race-route map builder.

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages