Skip to content

Repository files navigation

Stargate dialing computer

▶ Live demo

Deploy to GitHub Pages Code: MIT Art: CC BY-SA 3.0 Roadmap

What this is

A working Stargate you can operate in a browser.

In Stargate the device is a standing ring carved with symbols. You dial a destination by turning the ring until each symbol in turn lines up under a fixed chevron at the top, which locks; when the whole address is locked the gate opens onto a shimmering event horizon, and an armoured iris can be closed over it to block anything coming through.

This project is that machine as a web page. Pick eight symbols from the keypad — with the mouse, by typing their numbers, or from the address book — and the ring turns to each one in order, locks a chevron, and lights the symbol blue on both the ring and the pad. With all eight locked, RUN opens the gate onto a shifting event horizon. IRIS closes twenty-four blades over it. CLS spins the ring down and releases the address.

Or switch AUTO off and dial it by hand, one symbol at a time, forwards or back.

Not every address goes somewhere. Seven chevrons will lock over any symbols at all, but the eighth only engages if the eight together are a place. And the gate can ring: press INC and someone dials you — at which point the iris stops being a toggle and becomes a decision, since whether it is shut when the wormhole connects is the whole of it.

The symbols are not the ones from the show. They are a 64-symbol alphabet I designed — each one a 5 × 5 arrangement of 2 × 2 blocks — and an address is eight of them rather than the show's seven-of-thirty-nine. The ring had to be redivided into 64 cells to hold them.

It is built with no framework and no runtime dependencies: one page, one stylesheet, and about 750 lines of hand-written JavaScript, assembled from its parts by a dependency-free sixty-line build script. Every animation is SMIL<animateTransform> elements constructed in JavaScript and triggered with beginElement(). That was the point of the exercise: to find out how far SVG could be pushed with no canvas and no game engine.

The gate open, with all eight symbols locked

Controls

Button Key What it does
any symbol digits, then Enter Adds it to the address. Click a symbol again to remove it — but only if the ring has not locked it in yet.
AUTO A On by default. The ring chases your selections as you make them. Turn it off and the ring stops; REV and FWD come alive.
RUN Enter With all eight symbols locked, opens the gate. With an incomplete address, blinks red once for every symbol still missing.
IRIS I Closes or opens the 24-blade iris over the event horizon.
REV Gives the last chevron back and turns the ring to the symbol before it. Only while AUTO is off.
FWD Dials the next symbol in the address and locks it — one symbol, then it stops. Only while AUTO is off.
SLOW / NORM / SNAP S Dial speed. SLOW is the original 2021 timing, NORM is the default and half of it, SNAP is nearly instant. Starts on SNAP if your system asks for reduced motion.
DHD D Swaps the keypad for a dialling pedestal, and back. Both are live the whole time — they are two views of one address, so whichever you are not looking at is already up to date when you come back.
Settings, in the top corner of the page — sound on/off and volume. Outside the console, because the control row is full and a preference is not a thing you do to the gate.
CLS Esc Spins the ring down and releases the address. While the gate is open it arms a clear instead, which fires when the gate closes.
Destination Picks a known address and dials it. Built from the address book in model.js, so the menu and the gate can never disagree.
INC W Someone dials in. The ring dials an address that is not yours and the chevrons lock blue; shut the iris before it connects, or let it through.

Backspace takes the last symbol back — or deletes a digit, if you are part-way through typing one. The number you are typing shows in the readout under the keypad, and the line above it says what the gate is doing.

Nothing here talks to the dial directly. The keypad, the pedestal, the keyboard and the destination menu are input devices, and all four go through one command surface of ten verbs — see v0.8. It is why there are two pedestals rather than one, and why the built page contains no inline event handler at all.

Mid-dial, three chevrons locked The iris closed over the event horizon
Mid-dial — locked symbols turn blue on both the ring and the keypad The iris closed
RUN pressed with an empty address, blinking red The layout on a 390px phone
RUN with an incomplete address — one red blink per symbol still missing. REV, FWD and BTN are dimmed because AUTO is on 390 px phone: the control row reflows to two rows of four rather than scrolling sideways
The manual keys live, with AUTO off part-way down an address A symbol rendered red under the top chevron as CLS releases it
AUTO off: the ring stopped after four chevrons and REV/FWD took over CLS releasing a symbol — it holds red under the top chevron before it goes
An incoming wormhole part-way in An address that goes nowhere
An incoming wormhole: blue chevrons, and the iris still a choice An address that goes nowhere — seven chevrons lock, the eighth refuses
The ring band, cut into 64 cells The shut iris and its star
The ring is a band of 64 cells with a symbol standing in each — generated from the same files as the keypad The shut iris: 24 blades sealing the event horizon, and the star where their tips pass each other
The DHD part-way through an address The keypad and the DHD showing the same address
The DHD: three segmented bands with a symbol standing in each cell — the gate's own ring, three times over Both pedestals at once, which the page never does — the same eight symbols blue on each, because neither is the master
The settings panel
Settings, from the button in the top corner. Twelve voices, all synthesised in the page

Status

Active, and being developed. Current release: v0.6. The core works and has always worked: it loads with no console errors and no failed requests, and every control does what it says.

Each release since picking the 2021 project back up has had one job. v0.2 was a correctness pass over every defect that stood against v0.1. v0.3 wrote the controls that were never written — the manual dial that turning AUTO off has always implied, a speed control, keyboard operation, and a keypad that fits a phone. v0.4 made the thing maintainable: one copy of each drawing, tests, and a project layout that says what is a source and what is output. v0.5 gave it a point — somewhere to dial, and something that can dial you. v0.6 made the gate draw itself: the ring of symbols and the iris are computed now, not drawn. Measured before and after (CHANGELOG.md):

v0.1 v0.2 v0.3 v0.4.x
symbols ever rendered red as CLS releases them 0 8 8 8
fill attributes written to the DOM per clear 22,243 364 364 364
sweep to reach symbol 1 from rest +357.2° (10.9 s) −2.8° (1.1 s) −2.8° −2.8°
turning AUTO off actually stops the dial no yes yes yes
…and there is a way to carry on by hand no no yes yes
a full eight-symbol dial 34.2 s 24.0 s 12.1 s (1.4 s on SNAP) 12.1 s
prefers-reduced-motion honoured no no yes yes
operable from the keyboard no no yes yes
keypad overflow on a 320 px phone 88 px 0 0
copies of the gate drawing 2, drifted 2, drifted 2, drifted 1
hand-maintained HTML 539 KiB 539 KiB 539 KiB 3 KiB
keypad buttons a screen reader can name 0 / 64 0 / 64 0 / 64 64 / 64
control keys that render the same in Chromium and Firefox no no no yes
tests 0 0 0 68, in CI
addresses that open the gate all all all 7 (v0.5)
the gate can be dialled at you no no no yes (v0.5)
a shut iris seals the event horizon 99.3% 99.3% 99.3% 100% (v0.6)
blade tips visible in the star ~12 / 24 ~12 / 24 ~12 / 24 24 / 24 (v0.6)
copies of the 64-symbol alphabet 2 2 2 1 (v0.6)
a closed iris you can see no no no yes (v0.4.1)
…that survives a speed change n/a n/a no yes (v0.4.1)

Nothing is open — every issue raised since v0.2 is measured and closed. v0.7 made the gate plural: adding a Pegasus or Universe ring is a folder and a manifest, not an edit to every tool. v0.8 separated the input device from the dial, and the DHD it made possible is here. v0.9 gave the chevrons a sound — synthesised in the page, because there is not an audio file in this repository. Next is v1.0, which adds nothing and checks everything.

  • ROADMAP.md — what is planned, in order.
  • KNOWNISSUES.md — nothing open; eighteen closed since v0.2, each reproduced and measured rather than guessed at, with their numbers.
  • CHANGELOG.md — what changed per release.
  • test/README.md — what is tested, and the three different questions.

History

Written over eight days in April 2021, in six commits. Picked up again in 2026: v0.1 was a tidy pass that changed no behaviour — dead code and a committed copy of jQuery that nothing imported removed, 65 stray <?xml?> declarations and 68 duplicate element ids cleaned out of the markup, spelling fixed in the JavaScript API, and the position: absolute; left: 500px layout replaced with flexbox. v0.2 then fixed the defects that tidy had catalogued, v0.3 built the controls the original never had, and v0.4 gave it a build, tests and a structure. The 2021 state is tagged v0.1-original; the full list is in CHANGELOG.md.

Running it

git clone https://github.com/Dimitriuses/Stargate.git
cd Stargate
npm run build            # assembles dist/ — no dependencies needed for this
npm run serve            # or serve dist/ with anything you like

Then open http://localhost:8000/. Opening dist/index.html directly works too — every path is relative, so it loads under file:// as well (checked in Chromium: 0 failed requests).

The page has no runtime dependencies: dist/ is plain HTML, CSS, JS and SVG. The build itself needs nothing installed either — it is plain Node with no imports beyond fs and path. Only the tests need npm install.

Editing the artwork

The gate is assembled from parts, so each piece is small enough to actually open and edit. Every part is a standalone SVG that renders on its own.

Everything about one gate lives in one folder, and nothing about it lives anywhere else.

src/index.html                     the page skeleton, and its build markers      3 KiB
src/gates/milkyway/
  gate.js       the manifest: 64 symbols, an address of 8, every dimension       4 KiB
  gate.svg      the shell: root <svg>, paint order, markers                      2 KiB
  gate.css      every class the drawing uses — one copy                          1 KiB
  portal.svg    the event horizon and its gradient                               1 KiB
  chassis.svg   the ring casing and the nine chevrons                          329 KiB
  glyphs/S1..S64.svg                the alphabet, one file per symbol
tools/geometry.js                  computes the iris and the ring of symbols
        ↓  npm run build
dist/                              the whole site

Painting order matters and is set by the markers in gate.svg: the portal has to be behind the iris, or the event horizon covers the blades meant to block it.

Two of those parts are computed, not drawn. The symbols on the ring are generated from the same glyphs/ files as the keypad, so the alphabet exists once; and the iris blades are generated so their geometry can be made to seal exactly. tools/geometry.js holds both, and test/geometry.test.js measures them — the blade parameters were chosen by sweeping them and measuring coverage, not by eye.

gate.js is where the numbers are. Nothing in tools/ or test/ knows how many symbols a gate has: the manifest says, and the generators, the model and the tests all ask. Adding a gate is a new folder under src/gates/tools/gates.js finds it by looking, npm run build gives it dist/<id>/, and all three test suites pick it up with no edit anywhere. That is checked rather than asserted: a testbed fixture gate with 36 symbols, an address of six and 18 blades is under test on every run and is skipped by the deploy build.

The build refuses to run if a part carries its own <style>, or uses a class gate.css does not define — so there stays exactly one copy of the drawing's styles, and a typo in a class name is a build failure rather than an invisible shape.

Before v0.4 the gate was pasted into index.html and kept as a separate file, and the two copies had already drifted apart (#9). design/ still holds the Illustrator sources, but the build never reads them.

Tests

npm test          # 82 unit tests, pure model + geometry — nothing to install, under a second
npm run test:e2e  # 47 browser tests in Chromium (builds first)
npm run test:all  # both suites

Full detail in test/README.md; the short version is that there are three kinds of question here and they need three different answers.

model.js is the half of the dial logic with no document in it, so it tests directly under Node. Several of those tests exist to hold an invariant that CLAUDE.md otherwise only states in prose — that no duration is ever zero, that SLOW is still the 2021 timing to the millisecond.

behaviour.test.js drives the real page. Every test carrying an issue number is there because that issue was once real, and fails against the build that shipped before the fix.

Neither of those answers did anything else move, which for an animation-heavy page is the question that matters most. That is test/checkpoints.js: a fixed ten-stop session that dumps the model state, the ring angle, the chevron classes, the address slots and the fill of every ring and keypad symbol at each stop. SMIL uses fill="freeze", so snapshots taken at settled moments are deterministic. It prints a diff for a human to read rather than a verdict, because what counts as a wanted change differs per release.

The record so far:

release checkpoints vs the release before what moved
v0.1 tidy 10 / 10 identical nothing — that was the point
v0.2 10 / 10 but for two fields both intended, both #6
v0.3 10 / 10 identical, at the new default speed and at SLOW nothing settled; only durations
v0.4 10 / 10 identical, plus the keypad to the same SHA-256 nothing — a generated page that renders like the hand-written one
v0.5 10 / 10 but for the address slots intended — the strip shows the symbols now
v0.6 10 / 10 identical nothing settled; the ring and iris are generated drawings of the same thing
v0.7 10 / 10 identical nothing — the gate became plural and the Milky Way did not move
v0.8 10 / 10 identical, on both gates, plus the keypad to the same SHA-256 nothing — input came apart from the dial and the dial did not notice
v0.9 10 / 10 identical, on both gates nothing — the gate got a voice and the checkpoints are deaf, which is the harness working

Two blind spots, both by design: a missing src and an empty one dump the same value, so it is blind to #5; and sampling at rest cannot see timing at all, which is why v0.3 could halve every duration and still score 10/10.

Credits

The 64-symbol alphabet is mine (design/synvols.ai), and so is the 24-blade iris (design/Diaphragm.ai).

The gate ring is not. It is a hand redraw of a Wikimedia Commons file by Max Willis and User:M.A.D.company, licensed CC BY-SA 3.0. None of the upstream path data survives here — a rotation-aligned raster comparison of the two ring chassis scores an IoU of 0.211 where a re-export would score about 0.95 — but it is recognisably the same design, so it is credited as a derivative and the artwork carries the upstream licence. The full measurement is in ATTRIBUTION.md.

Layout

src/                       everything that becomes the site
  index.html               the page skeleton, with two build markers
  index.js                 dialing logic and every SMIL animation, built in JS
  model.js                 the same logic minus the DOM: geometry, address, speed table
  index.css                layout
  favicon.svg
  console.js               the command surface: ten verbs in, one state snapshot out
  sound.js                 twelve synthesised voices — no audio files anywhere
  settings.js              the panel outside the console
  devices/keypad.js        the pad and the control row
  devices/dhd.js           the dialling pedestal
  devices/keyboard.js      digits, Enter, Esc, arrows
  devices/switcher.js      which pedestal is plugged in
  gates/<id>/              one gate: manifest, drawing parts, stylesheet, alphabet
    gate.js                what this gate is — the only registry there is
    glyphs/S1..SN.svg      its alphabet, one file per symbol

dist/                      the built site — generated, gitignored, deployed by CI

tools/gates.js             finds and validates the gates; nothing is listed twice
tools/build.js             assembles dist/, one folder per gate; no dependencies
tools/geometry.js          the ring and iris generators — no numbers of its own
tools/screenshots.js       regenerates docs/*.png from the real page
test/model.test.js         unit tests on the address model, per gate
test/geometry.test.js      unit tests on the ring and iris geometry, per gate
test/behaviour.test.js     47 browser tests in Chromium
test/checkpoints.js        the ten-checkpoint session — a diff, not a verdict
test/lib/harness.js        shared browser plumbing and the checkpoint dump

design/synvols.ai          Illustrator source for the alphabet          (mine)
design/Diaphragm.ai        Illustrator source for the 24-blade iris     (mine)
design/StarGate2.ai        Illustrator source for the ring redraw       (derivative)

CLAUDE.md                  architecture, invariants and gotchas for maintainers
docs/                      screenshots

Contributing

Issues and pull requests are welcome. Each entry in KNOWNISSUES.md says how its issue was reproduced and what the fix looked like, which is the house style: measure it before and after, and say what the number was.

If you touch the artwork, edit the parts under src/gates/<id>/, then run npm run build; the build refuses a part that carries its own <style> or uses a class its stylesheet does not define. If you change dialing behaviour, run npm run test:all, and say what test/checkpoints.js reported against the previous build.

Licence

Code — MIT. Gate ring artwork — CC BY-SA 3.0, derived from work by Max Willis and User:M.A.D.company. Stargate is a trademark of Metro-Goldwyn-Mayer; this is a non-commercial fan project with no affiliation to the rights holder.

About

Interactive Stargate in SVG — dial an 8-symbol address on a custom 64-glyph alphabet, lock the chevrons, open the gate, close the iris. No framework, no build step.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages