Русский · English · Español · 中文 · Português
«Space Now» — agent API
This page is for AI agents (and their humans). It explains how an agent connects to a live «Space Now» page (known.html) in a person's browser and what it can do there: everything the interface can do — open any object, fly the camera, scrub time, switch layers and filters, astronaut mode, find eclipses and planet parades, tell a story with voice and captions, show its own images, record video, read errors. Rendering happens in the person's browser (WebGL on their GPU); the agent sends commands and gets answers and screenshots.
1. Pairing
- The person opens the site and presses the round 🤖 button (bottom-right, above the astronaut) — the agent bar opens; «Turn the agent on» shows a code (20 characters, e.g.
r4fxjbjsxdy6ex3pf4hd). The code is the pass: whoever has it controls this tab. 🤖 only hides/shows the bar, the connection stays; «Disconnect» in the bar turns it off. The bar always has 📂 (open a story file or save the shown one — without an agent too) and «MCP ↗» (this page). - The person gives the code to the agent. The agent calls
connectwith it. - Permanent connection (optional): on the page — 📌 «Remember» in the agent bar (the code stays in this browser, the site connects by itself every time it opens); the agent —
connectwithsave: true(code in~/.orbit-agent.json, MCP connects on start). After that, just open the site. To forget: unpin 📌 and callforget. - «Disconnect» on the page (or the agent leaving) ends the session; the page then returns layers and interface to how they were
before the agent, stops music, voice, captions, images and scenarios, and removes landing sites (
sites), drawings and marks (draw,mark,flybys), media windows and own bodies (spawn) — regardless ofonstop. The camera, time and selection come back too (by default;onstop {restore: false}— keep what the agent showed). Exception:play/story runwithdetach: true— the show plays to the end, then rolls back.
Sound (voice, music) starts only after the person has clicked the page at least once — browser rule. The voice is off by default: the person turns it on with 🔊 in the agent bar (remembered); captions are always shown.
Show checklist — always do this
[
{"cmd":"onstop","args":{"restore":true}}, // first step: on end, Esc or ✕ everything comes back
{"cmd":"reset"},
{"cmd":"view","args":{"on":["oplanets"]}}, // only the layers you need: satellites, craft, orbits, constellations off
{"cmd":"hideUi","args":{"on":true,"labels":false}},
{"cmd":"time","args":{"at":"<moment>","rate":0}}, // set the time yourself; rate 0 — the lighting won't drift
… flight (fly) …
… the show …
]
- Don't rely on defaults: set layers, time, camera and interface explicitly.
- A view set with
view(or a story'sonfield) is kept byshow(keepView: false— the event's layers).fly,camera,sites,timedon't touch layers;galaxy,sceneandambientset their own view — callviewagain after them if needed. - After the show the scene must be as before it:
onstop {restore: true}plus remove your own things —sites {on: false},flybys {on: false},draw {clear: true},image {clear: true},media {close: true}. When the agent leaves they are removed anyway. - Check the frame with
screenshotbefore telling the person «look». - The
captionsits bottom-left: keeptitleshort, put long text intosubandtext. - A failing step in
playgoes tologs(level error), toplaywithout args (lastError: {step, cmd, error}) and to the player as «⚠ step N». An unknown command in steps is an error at once, before the start.
2. Transport
Both sides connect to a relay room on the same site: wss://<site>/agent/<code>?r=agent (the page uses
?r=page). The room forwards messages between the two; nothing is stored. JSON messages:
agent → page { "id": 1, "cmd": "go", "args": { "search": "?o=Mars" } }
page → agent { "id": 1, "ok": true, "result": … } | { "id": 1, "ok": false, "error": "…" }
room → both { "sys": "peer", "on": true|false } (the other side joined / left)
keepalive send { "sys": "ping" } every 30 s, the room answers { "sys": "pong" }
One connection per role (a new agent with the same code replaces the old one, close code 4000 — do not reconnect then).
Messages up to 4 MB. Sites: https://space.bl2.ru, https://space.bl2.net (same site, ru/en by default).
3. MCP server
Ready-made MCP server (stdio): scripts/agent-mcp.mjs in the orbit-proto repository (Node ≥ 20, dependency ws).
Every command below is a tool of the same name (hide_ui → page command hideUi), plus connect,
disconnect, help (this page as text; lang: en). Files: image takes path, record saves
to path, screenshot returns an image.
{ "mcpServers": { "orbit-space": { "command": "node", "args": ["scripts/agent-mcp.mjs"] } } }
Env: AGENT_SITE — default site, AGENT_CODE — connect at start.
Connecting without the MCP server (cloud agents)
- Many agents run in cloud sandboxes whose outbound network is limited to an allow-list, so
wss://space.bl2.rumay be unreachable. Workaround: open the WebSocket from any browser tab onspace.bl2.ru(this help page, for example) and send commands from there. Connection check:{"sys":"ping"}→{"sys":"pong"}. - The agent leaving =
playstops and the scene rolls back. An agent with short sessions (browser tools allow ~45 s per call) sends the show withdetach: true—play {steps, detach: true}orstory {action: 'run', story, detach: true}— and returns at once: the page plays the show itself and rolls back (withonstop) when it ends. Withoutdetachkeep the socket open until the scenario ends. - Give every command its own timeout (say 8 s): otherwise one unanswered command hangs the whole call.
// «start and return» — from the console of a tab on space.bl2.ru (or Node ≥ 22)
const code = '<code>', ws = new WebSocket(`wss://space.bl2.ru/agent/${code}?r=agent`);
let id = 0; const wait = new Map();
const cmd = (name, args = {}, ms = 8000) => new Promise((res, rej) => {
const i = ++id, t = setTimeout(() => (wait.delete(i), rej(new Error('timeout ' + name))), ms);
wait.set(i, (m) => (clearTimeout(t), m.ok ? res(m.result) : rej(new Error(m.error))));
ws.send(JSON.stringify({ id: i, cmd: name, args }));
});
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (!m.sys) wait.get(m.id)?.(m), wait.delete(m.id); };
ws.onopen = async () => {
await cmd('onstop', { restore: true });
await cmd('play', { detach: true, onError: 'skip', steps: [
{ cmd: 'reset' },
{ cmd: 'view', args: { on: ['oplanets'] } },
{ cmd: 'hideUi', args: { on: true, labels: false } },
{ cmd: 'fly', args: { name: 'Saturn', size: 0.5, lit: true }, wait: 6500 },
{ cmd: 'caption', args: { title: 'Saturn', sub: '1.4 billion km from the Sun' }, wait: 8000 },
] });
ws.close(); // the show plays on by itself, then puts everything back
};
4. Commands
Look and state
| cmd | args | what |
|---|---|---|
| state | time, rate, selected object, view centre, camera (yaw, pitch, dist km, fov), star nearby, keys of visible layers, canvas size px | |
| screenshot | width, quality, full | JPEG of the 3D scene. full: true — whole tab with interface (the person allows tab capture once via «📷» in the agent bar) |
| body | name | for framing: radius km, kind, parent, moons, camera angles, sunYaw/sunPitch (camera direction from which the body is fully lit), fov, distForSize {0.25, 0.5, 0.66, 0.8 → km}: distance at which the disc fills that share of screen height (= radius / tan(fov/2) / k) |
| where | name, at | position at a moment: km and AU from Sun and Earth, RA/Dec from Earth (J2000) |
| logs | level, last, clear | page console errors/warnings/info (ring buffer 300) |
| snapshot | full state (time, rate, camera, centre, selection, all layer checkboxes, interface hidden, astronaut link) — keep it to reproduce a bug | |
| restore | state | return to a snapshot |
View, camera, time, layers
| cmd | args | what |
|---|---|---|
| search | q, limit | search the object base like the search field: bodies, moons, spacecraft, nebulae and galaxies (when their layer is on), Earth satellites (name or NORAD), asteroids (name or number; the catalogue loads via the load-asteroids row). Returns rows with n |
| open | n | open a search result: select, card, flight — like clicking the row |
| go | search | open a view like a share link: ?o= body (Mars, Марс, ISS…), ?a= asteroid (number or name), ?s= satellite NORAD, ?g= galaxy, ?n= nebula, ?st= constellation star; extras d= camera km, v=yaw,pitch, t= ISO time |
| fly | name, size, lit, face, seconds | smooth flight to a body (like a click in the tree). size — share of screen height for the body (0.66 — two thirds), lit: true — from the lit side; framing takes ~2.5 s more after the flight (~6 s total, use wait ≥ 6500). face: "spot" — Jupiter: time rewinds until the Great Red Spot is on the day side |
| camera | yaw, pitch, dist, ms | camera around the view centre (rad, km). ms > 0 — move smoothly over ms. Example «fill 2/3 of the screen, lit side»: body → camera {yaw: sunYaw+0.4, pitch: sunPitch, dist: distForSize[0.66], ms: 3000} |
| time | at, rate, live, from, to | from, to, ms, ease — smooth scrub from date to date; at — Unix seconds or ISO moment (BC: -000500-01-01), speed (1 real, 3600 = 1 h/s, negative = back, 0 pause), live: true |
| view | on, set, orbits | view — standard keys for every toggle. No args — list {key, on}; on: [...] — switch everything off and only the listed on; set: {key: true|false} — switch only these; orbits: false — all orbit lines at once. Keys (every group also has <key>:orbits — its orbit lines): oplanets — Sun, planets and moons; odwarfs — dwarf planets and the largest asteroids; ohelio — heliopause; oshock — termination shock; osandbox — own bodies (spawn); oknown-small — known small ones (Apophis, Bennu…); ocomets — comets; ointerstellar — interstellar objects (1I, 2I, 3I); okuiper — Kuiper belt; ooort — Oort cloud; a0…a9 — asteroids by size (a0 — over 100 km); ocraft, ocraft-orbit, ocraft-l — spacecraft in flight, in orbit around bodies, at Lagrange points; s0…s30 — Earth satellite groups, sats — all at once; ogalaxy — Milky Way model; ouniverse — galaxies; stars — constellation stars with names; conlines — constellation figure lines (with stars; not listed in view {on} — no lines); galmarks — Galactic centre and apex labels; galsun — «Sun — you are here»; scenelabels — labels only on what the scene needs (labels {only}). A single interstellar path: switch ointerstellar:orbits off, select {name: '3I/ATLAS'} — its path bright. Also: earth:ph|cities|seas|mountains|volcanoes, weather, aurora (aurora), sw (space weather), sk:0…6, sk:neb. Stories and slides take an on field (applied before the steps): list only what is needed — new layers that are on by default will not show up in old stories |
| layers | all layer groups: key, name, visible, orbits, count | |
| star | name | hip | gaia | index, fly | a star from the known-stars database (Gaia + Hipparcos): a famous one by name (Sirius, Proxima…), by Hipparcos number (hip) or Gaia DR3 id (gaia, as a string). Flies there by default (fly: false — card only); up close it is a sphere, the card shows the spectrum. The first call downloads the database (a few MB). The dots of the other stars are the ostarsdb layer |
| layer | key, visible, orbits | switch a layer. Keys: oplanets, odwarfs, ocomets, ocraft*, ogalaxy, ouniverse, ostarsdb — known-stars database (Gaia + Hipparcos, ~470k; the first switch-on downloads the data and the reply comes once it is loaded; warm it up with warm {code:"starsdb:"}), a0…a9 asteroids by size, s0… satellite groups… |
| galaxy | far | view of the Milky Way (far — with other galaxies). For scenes: dist (0 — model at the Sun, > 0 — fly out), pitch, center: true — orbit around the Galactic centre, off: true; Galaxy time — galtime {rate} (years/s) |
| galaxies | count, dwell | fly through N famous galaxies by distance, dwell seconds each |
| clear | deselect, close card |
Events (computed by the page, no formulas needed)
| cmd | args | what |
|---|---|---|
| find | kind, from, to, total, count, min | lunar (total: true — «blood moon»), solar, parade (min planets 3…7) from any date; returns mid time, total?, lat/lon/region where visible; lunar — contact (umbra touch) |
| show | kind, at, keepView | show an event like «📅 Events»: time — half an hour before mid, speed ×100 (set time {rate: 0} or your own), selection cleared, view centre and camera on the Earth (solar: above the shadow point) or the Moon (lunar); a parade — first-person view from the Earth, planets only. Layers set with view stay (keepView: false — the event's). After it: hideUi, camera if needed |
Whole interface
| cmd | args | what |
|---|---|---|
| ui | text, within | every visible button, checkbox, slider, field, list/tree row with ref, text, id, data-attributes, state. Filter by text or CSS area |
| click | ref | selector | press it |
| set | ref | selector, value | checkbox true/false, slider, field, select |
| key | code, ms, shift, ctrl | key press (KeyW… hold ms to fly, Space pause, KeyH hide panels, Escape…) |
| pointer | action, x, y, to, dy | mouse on the 3D scene: click (select), dblclick (fly to), move (hover), drag (rotate) to [x,y], wheel (dy zoom) |
| hideUi | on, labels | hide everything: panels, corner buttons, the agent bar (labels: false — body labels too); only the scene, caption, images, media windows, marks (mark) and drawings of the agent stay. on: false — back |
| astro | action, name | astronaut: enter (where the camera is), goto name (fly there, orbit), land (stand on the ground), exit, link, state. Walk and look — key/pointer |
| eval | code | dev server only (npm run dev): run a JS expression in the page; window.__known holds state. Not available on the public site |
Telling a story
| cmd | args | what |
|---|---|---|
| caption | title, sub, text, seconds | caption bottom-left like the screensaver (empty title — hide) |
| say | text, title, sub, audio, keep, caption | show the text as a caption; audio — an mp3 voice-over (link or a file name on the computer; no browser voice — make the mp3 beforehand, e.g. edge-tts); 🔇 — silent |
| music | on, theme, volume | background music generated in the browser: theme 0 «Nebula», 1 «Depth», 2 «Crystals», null — rotate |
| image | id, src | path, at, x, y, dx, dy, w, label, remove, clear | your picture over the scene: attached to a body (follows it) or at a screen point |
| play | steps | stop | pause | seek | step | a whole scenario played by the page: [{cmd, args, wait}], wait = ms or "speech". Like a player: pause, seek: n — jump to step n (earlier steps run at once, without waits or voice), step: ±1. The person gets a player at the bottom: ⏮ ⏪ ▶/⏸ ⏹ ⏩ ⏭, «slide · step», 💾 save on the site (password), ⬇ download, 🗑 delete, ✕. Stops on disconnect — except with detach: true (plays to the end, then rolls back). onError: 'skip' (default — go on) | 'stop'; a failing step goes to logs, to play without args (lastError) and to the player. A presentation moves to the next slide by itself; ✕ or Esc — exit (the interface comes back) |
| deck | slides, start, go, close | presentation: [{title, steps}]; the person gets «◀ n/N ▶» at the bottom (and PageUp/PageDown), moving plays that slide’s steps. Stays visible with hideUi |
| buttons | items | your own buttons at the bottom: [{label, title, steps}], a click plays the steps; [] removes |
| story | action, story, id, password, name | a story = what the agent composed (flight plan, narration, visualisation — e.g. for NASA Space Apps): {title, summary, lang, slides:[{title, steps}] | steps}. run — show; publish — save on the site with an edit password (password; none — the page asks the person) → link known.html?story=<uuid>, anyone views it without an agent (with «◀ n/N ▶»); up to 256 KB, kept 30 days after the last opening; update — edit (id + the same password); delete — delete from the site (id + the same password); download — a file for the person (any size); open — the person picks a file (or drops a .json on the page; «📂» menu in the agent bar); load — by id |
| steps | where we are in a story or presentation: slide and slide list, step and step list (n, label), playing, paused. Jump — play {seek: n} / {step: ±1}, deck {go: n}. The person has ☰ in the player — the list of slides and steps, click to jump | |
| scene | code | an ambient-mode scene inside a story — its camera flight, caption and time (~20–30 s, waits for the end). Codes — scenes: body:Сатурн, event:<id>, mission:…, astro:…, lapse:…, galaxy, pullback. The ambient mode («∞») itself is the same scene generator in the common player: ⏮ ▶/⏸ ⏭, ☰ played and all scenes, 💾/⬇ — what was played as a story of scene steps |
| scenes | all ambient scenes by section, with codes | |
| record | action, fps, mbps / path | video of the scene: start, stop (the MCP saves WebM to path) |
One entry point: WS, MCP, console, story
Commands and the language are the same wherever they come from: WebSocket from any language, MCP, the page console (the >_ button in the agent bar) or a story. Write a whole scenario and give it to story, or call commands one by one. New: exec {steps} — run language steps now (variables persist between calls), expr {e} — evaluate an expression, js {code, timeout} — JavaScript in a sandbox (isolated iframe + Web Worker: no DOM, cookies or network; inside — await cmd(name, args), await expr('…'), log(), sleep(), return).
Console: fly Mars size=0.6 (a command, the first word is name), a JSON command, steps {…} / […], an expression = …, code js ….
Base script — Node.js:
// node agent.mjs <код> (Node ≥ 22 — WebSocket встроен)
const code = process.argv[2], ws = new WebSocket(`wss://space.bl2.ru/agent/${code}?r=agent`);
let id = 0; const wait = new Map();
const cmd = (name, args = {}) => new Promise((res, rej) => {
const i = ++id; wait.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, cmd: name, args }));
});
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.sys) return; // peer / pong
const w = wait.get(m.id); wait.delete(m.id);
m.ok ? w.res(m.result) : w.rej(new Error(m.error));
};
setInterval(() => ws.send('{"sys":"ping"}'), 30000);
ws.onopen = async () => {
await cmd('fly', { name: 'Mars', size: 0.6, lit: true });
console.log(await cmd('expr', { e: "round(au(get('Mars','sun')),3)" }));
await cmd('exec', { steps: [{ wait: "dist('camera','Mars') < 1e5 km" }, { cmd: 'caption', args: { title: 'Mars' } }] });
process.exit(0);
};
Python:
# python agent.py <код> (pip install websockets)
import asyncio, json, sys, websockets
async def main(code):
async with websockets.connect(f"wss://space.bl2.ru/agent/{code}?r=agent") as ws:
n = 0
async def cmd(name, **args):
nonlocal n; n += 1
await ws.send(json.dumps({"id": n, "cmd": name, "args": args}))
while True:
m = json.loads(await ws.recv())
if m.get("id") == n:
if not m["ok"]: raise RuntimeError(m["error"])
return m["result"]
await cmd("fly", name="Saturn", size=0.6, lit=True)
print(await cmd("expr", e="get('Saturn','moons')"))
asyncio.run(main(sys.argv[1]))
Scenario language v2
Versions: a story without v (or v: 1) — steps and slides as above; v: 2 — a language with conditions. A new handler version never changes old ones.
{ "v": 2, "title": "…", "on": ["oplanets"], "vars": { "n": 0 }, "steps": [
{ "cmd": "fly", "args": { "name": "Mars", "size": 0.5, "lit": true } },
{ "wait": "dist('camera', 'Mars') < 50000 km", "timeout": 20 },
{ "set": { "n": "n + 1" } },
{ "if": "visible('Mars')", "then": [ { "cmd": "caption", "args": { "title": "Mars", "sub": "{round(au(dist('camera','Sun')),3)} AU from the Sun" } } ] },
{ "repeat": 2, "do": [ { "wait": 1 } ] }
] }
Steps: {cmd, args, wait} (wait — ms or an expression), {wait: expression | seconds, timeout}, {if, then, else}, {repeat: n | expression, do}, {set: {name: expression}}, {on: [...]}. Expressions — no eval: numbers with units km au ly m, strings '…', variables, + - * / %, < > <= >= == !=, && || !; functions dist(a, b) ('camera' or a body, km), visible(body), elapsed(), time(), camdist(), au(x), ly(x), round(x, d), abs min max. Properties on the fly: get(body | 'camera', 'prop') — a body has name, kind, radius, parent, moons, x y z, sun (km from the Sun), earth, camera (km from the camera), center, visible; the camera has dist, yaw, pitch, fov, x y z, sun, center; without a prop — the whole object. A command result into a variable: {cmd, args, as: 'r'}, a field — field(r, 'rows.0.text'), size — count(x). In args strings — {expression}. Errors go to logs. Next: triggers (when/once/every) and a sandbox (spawn bodies and 3D models). Own functions: {def: name, params, e} — in expressions, {def, params, steps} — as a step {call, args, as}, {local}, {return}; in a story — funcs. More functions: angle(a, b) — degrees between directions from the camera, edge(a, b) — how many degrees a shows past the limb of b, sin cos (degrees), pow sqrt. wait: true on a command — until it finishes. For scenes: fitdist(r[, margin]) — the distance from which a circle of radius r km fits the screen (narrow side; a body also has day — rotation period, s); satcount('all' | 'active' | 'debris' | 'rb' | 'dead') — catalogue objects around Earth; mstat(craft, 'launch' | 'km' | 'turns' | 'rmin' | 'rnow' | 'flybys') — mission numbers from its trajectory, as of today. Spacecraft trajectories and flybys come from the import (scripts/fetch-missions.mjs, JPL Horizons, daily): a new craft in the base gets its flybys and numbers automatically; flybys {craft} draws the flyby points on its path. Media in scenes: media {url | file, title, x, y, w, h, volume, loop, start, id} — a window with audio or video: an mp3/video link or YouTube; file — a file from the person's computer (the site stores no files: the person picks it in the window, then it plays offline); close: true closes the windows. State: remember {name} — save the camera, time, view and interface; recall {name} — bring it back; onstop {restore: true} (in a story file — the field "onstop": "restore") — on end, Esc or ✕ put everything back as before the start. Tell the person: stories and the screensaver can be driven by a remote — a presentation clicker (PageDown/PageUp, «.» or B — black screen), media keys on keyboards, headphones and TV boxes (next, previous, pause), a gamepad (D-pad ←/→, A — pause, B — black screen), keyboard arrows, Space, Esc. Direct links: ?ambient — the screensaver, ?ambient=<scene code from ☰> — starting from that scene (e.g. ?ambient=landings:Moon:apollo), ?story=<id>&step=N or &slide=N — a published story from that point; the 🔗 button in the player copies a link to what is playing. The ambient {play: code} command does the same from an agent; reset resets camera, time and view before a scene.
Camera and motion
| cmd | args | what |
|---|---|---|
| fly | name, ms, ease, size, alt, lat, lon, rel, lit | One-motion flight straight into the framing: size — share of screen height for the disk, alt — km above the surface, lat/lon — above a surface point, rel: {to, deg, elev} — side relative to another object (0° — it is behind the camera, 180° — behind the body in frame; elev — raise). Returns ms. |
| camera | dist, yaw, pitch, rel, ms, ease, target | Camera around the view centre; rel as in fly. target: {between: [a, b], k} — look at a point between bodies; {sat: 25544} — orbit around a satellite (NORAD); {sat, space: {tilt, spin, from, ms}} — view from space (Earth behind the satellite): tilt° from vertical, spin °/s, approach from from km to dist over ms; null — back to the body. |
| media | url | file, kind, title, x, y, w, h, volume, loop, start, id, anchor, dx, dy, header, close, forgetDir | A window with audio or video: a link (mp3/video/YouTube) or file — a file on the computer (the person picks it). anchor: {sat: NORAD} | {body: name} — the window follows an object (dx, dy — offset, px); header: false — no title bar (default when anchored). Returns ms for wait: true. close: true | id; forgetDir: true — forget the media folder. Before a story starts — the «Preparing the scene» window (files, link preload). |
| ambient | play | Screensaver «∞» from a given scene: code as in ☰ (hover|Mars, landings:Moon:apollo, event:eclSun, mission:Parker, satellites…); without play — the list of codes. |
| lang | set | Interface language: without set — the current one and the list (ru, en, es, zh, pt); set — switch. Switches live, without a reload: the agent stays connected. The screensaver replays the current scene in the new language; the player, the card, the layer tree and the windows are redrawn. The page address becomes /<lang>/ (for example /es/; the domain’s own language is just /) — the language is set by the address only, there is no ?lang=; a link to the site in a given language is https://space.bl2.net/<lang>/ (Russian — https://space.bl2.ru/). |
| reset | — | Before a scene: stop orbit, look and time ramps; Galaxy off; marks, flyby points and media windows removed (screensaver scenes do it themselves). |
| remember / recall | name | Save the state (camera, time, view, interface) / bring it back. |
| onstop | restore | On by default: on end, Esc, ✕ or the agent leaving, put everything back as before the agent's first command: layers and view toggles, panels and hidden interface, camera (angle, distance, centre, tracking), selection, time and speed (or «live»); music and voice stop. The agent's own things (sites, draw, mark, flybys, image, media, spawn) are always removed. restore: false (in a story file — "onstop": "keep") — keep the camera, time and selection as the agent left them. exec rolls back at its end only with an explicit onstop {restore: true}. |
| console | open | Open / close the >_ console. |
| sites | body | code, group, ids, light, on | Landing sites: cards at surface points (in the scene). body may be omitted — the body comes from the group (apollo, luna, rovers, new, farside, first, china — Moon; mars, chryse — Mars; venera — Venus) or from ids. Example: sites {group: 'apollo', light: true} → {lat, lon, alt, at, ids}; then time {at, rate: 0} and fly {name: 'Moon', lat, lon, alt}. Without flying to lat/lon the camera may face the far side — no cards visible. light: true — a time when the sites are in daylight (reply at). |
| flybys | craft, on | Gravity assist and orbit insertion points on a craft's path (from the mission trajectory). |
| orbit | speed (°/s), turns | ms, pitch, dist, end, blend, ease, stop | Orbit around the body. end: {to, deg, elev} — finish exactly on that framing (one direction, no going back). Ending at speed — it coasts, the next fly takes the speed over. |
| look | to, ms, ease, clear | Look at an object: the eye stays (or keeps orbiting), the view turns smoothly and tracks it. clear: true — back to the centre. |
| select, shadow, camera target | name; name, by, center, ms, dist; target | For screensaver scenes: select {name} — highlight a body's path ('' — clear); shadow {name, by, center, ms, dist} — camera on the eclipse shadow spot; camera {target: {between: [a, b], k}, ms} — look-at point between bodies (orbit circles it), target: null — back; look {to: 'galcenter'}; fly {frame: {path, from, to, with}, elev, deg} — frame a craft's whole path. |
| follow | name, ms, dist, ease | Follow another object: smooth hand-over keeping the framing. |
Showing a small body (a comet, an interstellar object — invisible from tens of millions of km): follow {name} or fly {name}, then mark {name, label: true} — a blinking multicoloured marker at the body (visible with hideUi too; also color, speed), select {name} — its path bright. Remove: mark {remove: name} or mark {clear: true}, select {name: ''}; the onstop rollback and the agent leaving remove marks by themselves.
Easing ease — like a CSS timing function: linear, ease, ease-in, ease-out, ease-in-out, smooth, cubic-bezier(x1,y1,x2,y2) or a formula of t (0…1): 1-pow(1-t,2), t*t*t*(t*(6*t-15)+10).
Overlap: {cmd, args, then: [steps], before_end: ms | after_begin: ms | when: "condition"} — nested steps run alongside the command. Example: orbit the Earth, turn to Io, fly to it:
[ {"cmd": "fly", "args": {"name": "Earth", "size": 0.4, "rel": {"to": "sun", "deg": 0, "elev": 10}, "ms": 3000, "ease": "ease-in-out"}, "wait": true},
{"cmd": "orbit", "args": {"speed": 60, "ms": 6000, "ease": "ease-in"},
"when": "edge('Io','Earth') < 15", "then": [{"cmd": "look", "args": {"to": "Io", "ms": 1500}, "wait": true}], "wait": true},
{"cmd": "fly", "args": {"name": "Io", "size": 0.15, "rel": {"to": "Jupiter", "deg": 180, "elev": 15}, "ms": 8000, "ease": "ease-out"}, "wait": true} ]
Sandbox, drawing, astronaut
| cmd | args | what |
|---|---|---|
| spawn | id, like, diameter, color, at: {near, dist, dir} | pos, vel | Your own object: a copy of a body or a rock; it takes a circular orbit around the dominant body (physics, not straight lines) |
| move | id, vel | impact: {target, at} | New velocity or «hit a body at a moment» |
| despawn / objects | id | all | Remove / list |
| draw | items | clear | remove | Graphics in space for debugging and lessons: arrow (vel, sun, north, {sum}), axes (roll/pitch/yaw), line, dash, path, angle (arc and degrees), project (projection onto a body, altitude), point, text; label — text along the line |
| flybys | craft, on | Gravity-assist points on a spacecraft's path: «Body · date» and what the assist gave; on: false — remove |
| sites | body | code, group, ids, light, on | Landing sites (Moon, Mars, Venus, Titan): points and in-scene cards — photo, name, year, description; group — combined view (apollo, luna, rovers, new, farside, first, china, mars, chryse, venera; the body follows from the group, body optional); light: true — sites on the day side; returns lat, lon, alt for fly; on: false — remove |
| astro | action: goto | view | shield | land | exit, name, tpd, yaw, pitch, on, text | Astronaut: anchor to anything (body, ISS, NORAD number, asteroid); view — look at the figure; shield — remove/set a shield, own message |
Page console — the backquote key (ё on a Russian layout).
How to work
- Read this page (
help),connectwith the code. - Ask the page for data:
search/open(objects),body(sizes, lit side),where(positions),find(events),layers,state. - Prepare the actions from that data: one
playscenario, or adeckof slides the person steps through, orbuttons. - Run it; check with
screenshotandlogs; adjust.
5. Example: «tell me about the planets»
connect {code}
layers → layer {key, visible:false} for everything except oplanets
music {theme: 0, volume: 0.4}; hide_ui {on: true}
play {steps: [
{cmd:"fly", args:{name:"Mars"}, wait: 3800},
{cmd:"camera", args:{yaw: …, pitch: …, dist: …, ms: 3000}, wait: 3200}, // values from body {name:"Mars"}
{cmd:"say", args:{title:"Mars", sub:"228 million km · light takes 13 min", text:"…", audio:"mars.mp3"}, wait: "speech"}, // voice is Piper in the browser (ru ruslan, en amy); audio — a ready mp3 instead
…
]}
6. Resources
- Bodies: Sun, 8 planets with moons, dwarf planets, ~1.4 M asteroids (by size groups), comets, interplanetary spacecraft, ~30 000 Earth satellites (CelesTrak, daily), constellations in 3D, Milky Way model with nebulae and clusters, 44 000 galaxies (2MRS).
- Names work in Russian and English (
Марс/Mars); positions are J2000 equatorial, km. - Time range: any date (ephemerides degrade far from today; satellites only ±90 days from the catalog).
- Share links (
?o=…&d=…&v=…&t=…) open the same view for anyone.
Rules: the page accepts only these commands; no navigation to other sites, no personal data. Keep the person in control — they can press «Disconnect» any time.