Русский · English · Español · 中文 · Português
«Космос сейчас» — API для агентов
Эта страница — для ИИ-агентов (и их людей). Здесь описано, как агент подключается к живой странице «Космоса сейчас» (known.html) в браузере человека и что он там может: всё, что умеет интерфейс, — открыть любой объект, вести камеру, крутить время, включать слои и фильтры, режим космонавта, искать затмения и парады планет, рассказывать голосом с подписями, показывать свои картинки, писать видео, читать ошибки. Рисует браузер человека (WebGL на его видеокарте); агент шлёт команды и получает ответы и снимки.
1. Подключение
- Человек открывает сайт и нажимает круглую кнопку 🤖 (справа внизу, над космонавтом) — открывается полоска агента; в ней «Включить агента» — появляется код (20 знаков, например
r4fxjbjsxdy6ex3pf4hd). Код — это пропуск: у кого он есть, тот управляет этой вкладкой. 🤖 только прячет/показывает полоску, связь остаётся; выключить — «Отключить» в полоске. В полоске всегда есть 📂 (открыть файл сюжета или сохранить показанный — и без агента) и «MCP ↗» (эта справка). - Человек даёт код агенту. Агент вызывает
connectс этим кодом. - Постоянное соединение (по желанию): на странице — 📌 «Помнить» в плашке агента (код хранится в этом браузере, сайт подключается сам при каждом открытии), агент —
connectсsave: true(код в файле~/.orbit-agent.json, MCP подключается сам при запуске). Дальше достаточно открыть сайт. Забыть: снять 📌 иforget. - «Отключить» на странице (или уход агента) завершает сеанс: страница возвращает слои и интерфейс, как было до агента, глушит
музыку и голос, убирает подписи, картинки, места посадок (
sites), рисунки и метки (draw,mark,flybys), окна медиа, свои тела (spawn), останавливает сценарий и запись — независимо отonstop. Возвращаются и камера, время, выбор (по умолчанию;onstop {restore: false}— оставить, как показал агент). Исключение —play/story runсdetach: true: показ доигрывает, откат — по его концу.
Звук (голос, музыка) — только после того, как человек хотя бы раз щёлкнул по странице: так требует браузер. Голос по умолчанию выключен: человек включает его сам — 🔊 в плашке агента (запоминается); подписи видны всегда.
Чек-лист показа — делай всегда
[
{"cmd":"onstop","args":{"restore":true}}, // первым шагом: по концу, Esc или ✕ всё вернётся
{"cmd":"reset"},
{"cmd":"view","args":{"on":["oplanets"]}}, // только нужные слои: спутники, аппараты, орбиты, созвездия — выключены
{"cmd":"hideUi","args":{"on":true,"labels":false}},
{"cmd":"time","args":{"at":"<момент>","rate":0}}, // время задавай сам; rate 0 — свет не уедет
… перелёт (fly) …
… показ …
]
- Не полагайся на умолчания: слои, время, камеру и интерфейс задавай явно.
- Вид, заданный
view(или полемonсюжета),showне сбрасывает (keepView: false— слои как у события).fly,camera,sites,timeслоёв не трогают;galaxy,sceneиambientставят свой вид — после них задайviewснова, если нужно. - После показа сцена должна быть как до него:
onstop {restore: true}плюс сними своё —sites {on: false},flybys {on: false},draw {clear: true},image {clear: true},media {close: true}. При уходе агента это снимается и само. - Кадр проверяй через
screenshot, прежде чем сказать человеку «смотри». - Подпись
captionвыводится внизу слева: держиtitleкоротким, длинное переноси вsubиtext. - Ошибка шага в
play— вlogs(level error), в ответеplayбез аргументов (lastError: {step, cmd, error}) и в плеере «⚠ шаг N». Неизвестная команда в шагах — ошибка сразу, до запуска.
2. Связь
Обе стороны подключаются к комнате-ретранслятору на том же сайте: wss://<сайт>/agent/<код>?r=agent
(страница — ?r=page). Комната пересылает сообщения между ними и ничего не хранит. Сообщения — JSON:
агент → страница { "id": 1, "cmd": "go", "args": { "search": "?o=Mars" } }
страница → агент { "id": 1, "ok": true, "result": … } | { "id": 1, "ok": false, "error": "…" }
комната → обоим { "sys": "peer", "on": true|false } (собеседник подключился / ушёл)
поддержание связи { "sys": "ping" } раз в 30 с, комната отвечает { "sys": "pong" }
По одной связи на роль (новый агент с тем же кодом вытесняет старого, код закрытия 4000 — тогда не переподключаться).
Сообщение — до 4 МБ. Сайты: https://space.bl2.ru, https://space.bl2.net (один сайт, язык по умолчанию — ru / en).
3. MCP-сервер
Готовый MCP-сервер (stdio): scripts/agent-mcp.mjs в репозитории orbit-proto (Node ≥ 20, зависимость ws).
Каждая команда ниже — инструмент с тем же именем (hide_ui → команда страницы hideUi), плюс connect,
disconnect, help (эта страница текстом; lang: ru | en). Файлы: image берёт path,
record сохраняет в path, screenshot возвращает картинку.
{ "mcpServers": { "orbit-space": { "command": "node", "args": ["scripts/agent-mcp.mjs"] } } }
Переменные: AGENT_SITE — сайт по умолчанию, AGENT_CODE — подключиться сразу, AGENT_LANG — язык справки.
Подключение без MCP-сервера (облачные агенты)
- Многие агенты работают в облачных песочницах, где исходящая сеть закрыта списком разрешённых сайтов, и
wss://space.bl2.ruможет быть недоступен. Обходной путь — открыть WebSocket из любой вкладки браузера наspace.bl2.ru(например, этой справки) и слать команды оттуда. Проверка связи:{"sys":"ping"}→{"sys":"pong"}. - Уход агента = остановка
playи откат сцены. Агент с короткими сессиями (у инструментов браузера лимит ~45 с на вызов) отправляет показ сdetach: true—play {steps, detach: true}илиstory {action: 'run', story, detach: true}— и сразу возвращается: страница доиграет показ сама, а откат (иonstop) сделает по его концу. Безdetachдержи сокет открытым до конца сценария. - Каждой команде — свой таймаут (например, 8 с): иначе одна неотвеченная команда вешает весь вызов.
// «запустил и вернулся» — из консоли вкладки на space.bl2.ru (или Node ≥ 22)
const 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: 'Сатурн', sub: '1,4 млрд км от Солнца' }, wait: 8000 },
] });
ws.close(); // показ доиграет сам и потом вернёт всё как было
};
4. Команды
Посмотреть и состояние
| cmd | args | что |
|---|---|---|
| state | время, скорость, выбранный объект, центр вида, камера (yaw, pitch, dist км, fov), звезда рядом, ключи включённых слоёв, размер холста, px | |
| screenshot | width, quality, full | JPEG сцены 3D. full: true — вся вкладка с интерфейсом (человек один раз разрешает захват кнопкой «📷» в плашке агента) |
| body | name | для кадрирования: радиус км, вид, родитель, луны, углы камеры, sunYaw/sunPitch (откуда тело освещено целиком), fov, distForSize {0.25, 0.5, 0.66, 0.8 → км}: с какого расстояния диск займёт такую долю высоты экрана (= радиус / tan(fov/2) / k) |
| where | name, at | где тело в момент: км и а.е. от Солнца и Земли, прямое восхождение и склонение с Земли (J2000) |
| logs | level, last, clear | журнал консоли страницы: ошибки, предупреждения, info (последние 300) |
| snapshot | всё состояние (время, скорость, камера, центр, выбор, все галочки слоёв, спрятан ли интерфейс, ссылка космонавта) — сохранить, чтобы повторить баг | |
| restore | state | вернуть состояние из snapshot |
Вид, камера, время, слои
| cmd | args | что |
|---|---|---|
| search | q, limit | поиск по базе объектов, как поле поиска: тела, луны, аппараты, туманности и галактики (если слой включён), спутники Земли (имя или NORAD), астероиды (имя или номер; каталог грузится строкой load-asteroids). Ответ — строки с n |
| open | n | открыть найденное: выбор, карточка, перелёт — как щелчок по строке |
| go | search | открыть вид как по ссылке: ?o= тело (Mars, Марс, ISS…), ?a= астероид (номер или имя), ?s= спутник NORAD, ?g= галактика, ?n= туманность, ?st= звезда созвездия; добавки d= камера км, v=yaw,pitch, t= ISO-время |
| fly | name, size, lit, face, seconds | плавный перелёт к телу (как щелчок в дереве). size — доля высоты экрана под тело (0.66 — две трети), lit: true — с освещённой стороны; кадр — ещё ~2.5 с после перелёта (всего ~6 с, ставьте wait ≥ 6500). face: "spot" — у Юпитера время отматывается, пока Большое красное пятно не выйдет на дневную сторону |
| camera | yaw, pitch, dist, ms | камера вокруг центра вида (рад, км). ms > 0 — плавно за ms. Пример «тело на 2/3 экрана с освещённой стороны»: body → camera {yaw: sunYaw+0.4, pitch: sunPitch, dist: distForSize[0.66], ms: 3000} |
| time | at, rate, live, from, to | from, to, ms, ease — плавная прокрутка от даты до даты; at — секунды Unix или ISO-момент (до н. э.: -000500-01-01), скорость (1 — как в жизни, 3600 — час в секунду, отрицательная — назад, 0 — пауза), live: true — «вживую» |
| view | on, set, orbits | вид — единые ключи всех переключателей. Без аргументов — список {key, on}; on: [...] — выключить всё и включить только перечисленное; set: {ключ: true|false} — переключить только эти; orbits: false — все линии орбит разом. Ключи (у каждой группы есть ещё <ключ>:orbits — линии орбит): oplanets — Солнце, планеты и луны; odwarfs — карликовые планеты и крупнейшие астероиды; ohelio — гелиопауза; oshock — ударная волна; osandbox — свои тела (spawn); oknown-small — мелкие известные (Апофис, Бенну…); ocomets — кометы; ointerstellar — межзвёздные (1I, 2I, 3I); okuiper — пояс Койпера; ooort — облако Оорта; a0…a9 — астероиды по размеру (a0 — крупнее 100 км); ocraft, ocraft-orbit, ocraft-l — аппараты в полёте, на орбитах тел, в точках Лагранжа; s0…s30 — группы спутников Земли, sats — все разом; ogalaxy — модель Млечного Пути; ouniverse — галактики; ostarsdb — известные звёзды (Gaia, Hipparcos); earth:ph|cities|seas|mountains|volcanoes — подписи на Земле; stars — звёзды созвездий с подписями; conlines — линии фигур созвездий (вместе со stars; не перечислил в view {on} — линий нет); weather — облака Земли; aurora — полярное сияние; sw — солнечная погода (вспышки, выбросы); galmarks — подписи центра Галактики и апекса; galsun — «Солнце — вы здесь»; scenelabels — подписи только у нужного сцене (labels {only}); sk:0…6, sk:neb — виды объектов неба (галактики, скопления…, туманности). Путь одного межзвёздного тела: ointerstellar:orbits выключить, select {name: '3I/ATLAS'} — его путь ярко. У сюжета и слайда — поле on (перед шагами): только то, что нужно, — новые слои, включённые по умолчанию, в старых сюжетах не появятся |
| layers | все группы слоёв: key, имя, видно ли, орбиты, сколько объектов | |
| star | name | hip | gaia | index, fly | звезда из базы известных звёзд (Gaia + Hipparcos): знаменитая по имени (Sirius, Сириус, Proxima…), по номеру Hipparcos (hip) или Gaia DR3 (gaia, строкой). По умолчанию подлетает (fly: false — только карточка); вблизи — шар, в карточке спектр. Первый вызов качает базу (несколько МБ). Точки остальных звёзд — слой ostarsdb |
| layer | key, visible, orbits | включить/выключить слой. Ключи: oplanets, odwarfs, ocomets, ocraft*, ogalaxy, ouniverse, ostarsdb — база известных звёзд (Gaia + Hipparcos, ~470 тыс.; первое включение качает данные, ответ придёт после загрузки; прогреть заранее — warm {code:"starsdb:"}), a0…a9 — астероиды по размеру, s0… — группы спутников… |
| galaxy | far | вид на Млечный Путь (far — дальше, с другими галактиками). Для сцен: dist (0 — модель у Солнца, > 0 — отлёт), pitch, center: true — облёт вокруг центра Галактики, off: true; время Галактики — galtime {rate} (лет/с) |
| galaxies | count, dwell | пролёт по N известным галактикам по возрастанию расстояния, dwell секунд на каждую |
| clear | снять выбор, закрыть карточку |
События (считает страница, формулы не нужны)
| cmd | args | что |
|---|---|---|
| find | kind, from, to, total, count, min | lunar (total: true — «кровавая Луна»), solar, parade (min — планет 3…7) от любой даты; ответ — момент середины, полное ли, где видно (lat/lon/region); у лунных — contact (касание тени) |
| show | kind, at, keepView | показать событие как из «📅 События»: время — за полчаса до середины, скорость ×100 (поставь time {rate: 0} или свою), выбор снят, центр вида и камера — на Землю (солнечное: над точкой тени) или Луну (лунное); парад — вид с Земли от первого лица, только планеты. Слои, заданные view, не трогает (keepView: false — как у события). После: hideUi, при нужде camera |
Весь интерфейс
| cmd | args | что |
|---|---|---|
| ui | text, within | все видимые кнопки, галочки, ползунки, поля, строки списков и дерева — с ref, текстом, id, data-атрибутами, состоянием. Фильтр по тексту или CSS-области |
| click | ref | selector | нажать |
| set | ref | selector, value | галочка true/false, ползунок, поле, список |
| key | code, ms, shift, ctrl | клавиша (KeyW… — держать ms, лететь; Space — пауза; KeyH — спрятать панели; Escape…) |
| pointer | action, x, y, to, dy | мышь по сцене 3D: click (выбрать), dblclick (лететь), move (наведение), drag (вращать) до [x,y], wheel (dy — приблизить) |
| hideUi | on, labels | спрятать всё: панели, кнопки по углам, плашку агента (labels: false — и подписи тел); остаются сцена, подпись, картинки, окна медиа, метки (mark) и рисунки агента. on: false — вернуть |
| astro | action, name | космонавт: enter (где камера), goto name (подлететь, орбита), land (встать на грунт), exit, link, state. Ходить и смотреть — key/pointer |
| eval | code | только на dev-сервере (npm run dev): выполнить JS-выражение в странице; window.__known — состояние. На сайте недоступно |
Рассказ
| cmd | args | что |
|---|---|---|
| caption | title, sub, text, seconds | подпись слева внизу, как в заставке (пустой title — убрать) |
| say | text, title, sub, audio, keep, caption | вывести текст подписью; audio — озвучка mp3 (ссылка или имя файла на компьютере; голоса браузера нет — mp3 делают заранее, например edge-tts); 🔇 — тишина |
| music | on, theme, volume | фоновая музыка, собирается в браузере: theme 0 «Туманность», 1 «Глубина», 2 «Кристаллы», null — по очереди |
| image | id, src | path, at, x, y, dx, dy, w, label, remove, clear | своя картинка поверх сцены: у тела (едет за ним) или в точке экрана |
| play | steps | stop | pause | seek | step | сценарий целиком проигрывает сама страница: [{cmd, args, wait}], wait — мс или "speech" (пока не дочитает голос). Как плеер: pause, seek: n — на шаг n (предыдущие выполняются сразу, без ожиданий и голоса), step: ±1. У человека внизу плеер: ⏮ ⏪ ▶/⏸ ⏹ ⏩ ⏭, «слайд · шаг», 💾 сохранить на сайт (пароль), ⬇ скачать, 🗑 удалить, ✕. Останавливается при отключении агента — кроме detach: true (доиграет, потом откат). onError: 'skip' (по умолчанию — дальше) | 'stop'; ошибка шага — в logs, в play без аргументов (lastError) и в плеере. Презентация доиграла слайд — сама идёт к следующему; ✕ или Esc — выйти (интерфейс вернётся) |
| deck | slides, start, go, close | презентация: [{title, steps}]; у человека внизу «◀ n/N ▶» (и PageUp/PageDown), переход проигрывает шаги слайда. Видна и при hideUi |
| buttons | items | свои кнопки внизу: [{label, title, steps}], щелчок проигрывает шаги; [] — убрать |
| story | action, story, id, password, name | сюжет — то, что составил агент (план полёта, рассказ, визуализация — например для NASA Space Apps): {title, summary, lang, slides:[{title, steps}] | steps}. run — показать; publish — сохранить на сайте с паролем на правку (password; нет — страница спросит человека) → ссылка known.html?story=<uuid>, смотреть может любой без агента (с «◀ n/N ▶»); до 256 КБ, хранится 30 дней с последнего открытия; update — поправить (id + тот же пароль); delete — удалить с сайта (id + тот же пароль); download — человеку файлом (любого размера); open — человек выбирает файл (или перетаскивает .json на страницу; в плашке агента — меню «📂»); load — по id |
| steps | где сейчас в сюжете или презентации: слайд и список слайдов, шаг и список шагов (n, подпись), играет ли, пауза. Переход — play {seek: n} / {step: ±1}, deck {go: n}. У человека в плеере — ☰ список слайдов и шагов, щелчок — перейти | |
| scene | code | сцена заставки внутри сюжета — её пролёт камеры, подпись и время (~20–30 с, ждёт конца). Коды — scenes: body:Сатурн, event:<id>, mission:…, astro:…, lapse:…, galaxy, pullback. Сама заставка («∞») — тот же генератор сцен в общем плеере: ⏮ ▶/⏸ ⏭, ☰ сыгранные и все сцены, 💾/⬇ — сыгранное сюжетом из шагов scene |
| scenes | все сцены заставки по разделам с кодами | |
| record | action, fps, mbps / path | видео сцены: start, stop (MCP сохраняет WebM в path) |
Одна точка входа: WS, MCP, консоль, сюжет
Команды и язык одинаковы, откуда бы ни пришли: по WebSocket из любого языка, из MCP, из консоли страницы (кнопка >_ в полоске агента) или из сюжета. Можно написать готовый сценарий и отдать его story, а можно дёргать команды по одной. Новое: exec {steps} — выполнить шаги языка сразу (переменные живут между вызовами), expr {e} — посчитать выражение, js {code, timeout} — JavaScript в песочнице (изолированный iframe + Web Worker: без DOM, cookies и сети; внутри — await cmd(имя, args), await expr('…'), log(), sleep(), return).
Консоль: fly Марс size=0.6 (команда, первое слово — name), JSON-команда, шаги {…} / […], выражение = …, код js ….
Базовый скрипт — 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]))
Язык сценариев v2
Версии: сценарий без v (или v: 1) — шаги и слайды, как выше; v: 2 — язык с условиями. Новая версия обработчика старые не трогает.
{ "v": 2, "title": "…", "on": ["oplanets"], "vars": { "n": 0 }, "steps": [
{ "cmd": "fly", "args": { "name": "Марс", "size": 0.5, "lit": true } },
{ "wait": "dist('camera', 'Марс') < 50000 km", "timeout": 20 },
{ "set": { "n": "n + 1" } },
{ "if": "visible('Марс')", "then": [ { "cmd": "caption", "args": { "title": "Марс", "sub": "до Солнца {round(au(dist('camera','Солнце')),3)} а.е." } } ] },
{ "repeat": 2, "do": [ { "wait": 1 } ] }
] }
Шаги: {cmd, args, wait} (wait — мс или выражение), {wait: выражение | секунды, timeout}, {if, then, else}, {repeat: n | выражение, do}, {set: {имя: выражение}}, {on: [...]}. Выражения — без eval: числа с единицами km au ly m, строки '…', переменные, + - * / %, < > <= >= == !=, && || !; функции dist(a, b) ('camera' или тело, км), visible(тело), elapsed(), time(), camdist(), au(x), ly(x), round(x, d), abs min max. Свойства на лету: get(тело | 'camera', 'свойство') — у тела name, kind, radius, parent, moons, x y z, sun (км от Солнца), earth, camera (км от камеры), center, visible; у камеры dist, yaw, pitch, fov, x y z, sun, center; без свойства — весь объект. Результат команды — в переменную: {cmd, args, as: 'r'}, поле — field(r, 'rows.0.text'), размер — count(x). В строках args — {выражение}. Ошибки — в logs. Дальше: триггеры (when/once/every) и песочница (spawn тел и 3D-моделей). Свои функции: {def: имя, params, e} — в выражениях, {def, params, steps} — шагом {call, args, as}, {local}, {return}; в сюжете — funcs. Ещё функции: angle(a, b) — градусы между направлениями из камеры, edge(a, b) — на сколько градусов a видно за краем диска b, sin cos (градусы), pow sqrt. wait: true у команды — пока она доиграет. Для сцен: fitdist(r[, запас]) — с какого расстояния круг радиуса r км целиком в экране (по узкой стороне; у тела есть day — сутки, с); satcount('all' | 'active' | 'debris' | 'rb' | 'dead') — объекты каталога у Земли; mstat(аппарат, 'launch' | 'km' | 'turns' | 'rmin' | 'rnow' | 'flybys') — числа миссии из её траектории на сегодня. Траектории аппаратов и их пролёты — из импорта (scripts/fetch-missions.mjs, JPL Horizons, раз в сутки): новый аппарат в базе получает пролёты и числа сам; flybys {craft} — точки пролётов на пути. Медиа в сценах: media {url | file, title, x, y, w, h, volume, loop, start, id} — окно со звуком или видео: ссылка на mp3/видео или YouTube; file — файл с компьютера человека (сайт файлы не хранит: человек выбирает его в окне, дальше — без сети); close: true — закрыть окна. Состояние: remember {name} — запомнить камеру, время, вид и интерфейс; recall {name} — вернуть; onstop {restore: true} (в файле сюжета — поле "onstop": "restore") — при конце, Esc или ✕ вернуть всё как до запуска. Подскажи человеку: сюжет и заставку можно вести с пульта — кликер презентаций (PageDown/PageUp, «.» или B — чёрный экран), медиакнопки клавиатуры, наушников и ТВ-приставки (следующий, предыдущий, пауза), геймпад (крестик ←/→, A — пауза, B — чёрный экран), с клавиатуры — стрелки, пробел, Esc. Прямые ссылки: ?ambient — заставка, ?ambient=<код сцены из ☰> — с этой сцены (например ?ambient=landings:Moon:apollo), ?story=<id>&step=N или &slide=N — опубликованный сюжет с пункта; кнопка 🔗 в плеере копирует ссылку на то, что играет. Команда ambient {play: код} — то же из агента; reset — сброс камеры, времени и вида перед сценой.
Камера и движение
| cmd | args | что |
|---|---|---|
| fly | name, ms, ease, size, alt, lat, lon, rel, lit | Перелёт к телу одним движением сразу в ракурс: size — доля высоты экрана под диск, alt — высота над поверхностью, км, lat/lon — над точкой поверхности, rel: {to, deg, elev} — со стороны относительно другого объекта (0° — он за спиной, 180° — он за телом в кадре; elev — подъём). Ответ — ms. |
| camera | dist, yaw, pitch, rel, ms, ease, target | Камера вокруг центра вида; rel — как у fly. target: {between: [a, b], k} — центр взгляда между телами; {sat: 25544} — центр облёта на спутнике (по NORAD); {sat, space: {tilt, spin, from, ms}} — вид со стороны космоса (Земля за спутником): наклон от вертикали tilt°, кружение spin °/с, подлёт с расстояния from (км) до dist за ms; null — центр обратно на тело. |
| media | url | file, kind, title, x, y, w, h, volume, loop, start, id, anchor, dx, dy, header, close, forgetDir | Окно со звуком или видео: ссылка (mp3/видео/YouTube) или file — файл с компьютера (выбирает человек). anchor: {sat: NORAD} | {body: имя} — окно у объекта, ходит за ним (dx, dy — сдвиг, px); header: false — без строки заголовка (у привязанных — по умолчанию). Ответ ms — длительность для wait: true. close: true | id; forgetDir: true — забыть папку с медиа. Перед запуском сюжета — окно «Подготовка сценария» (файлы, предзагрузка ссылок). |
| ambient | play | Заставка «∞» сразу со сцены: код как в ☰ (hover|Mars, landings:Moon:apollo, event:eclSun, mission:Паркер, satellites…); без play — список кодов. |
| lang | set | Язык интерфейса: без set — текущий и список (ru, en, es, zh, pt); set — переключить. Переключает на лету, без перезагрузки: агент остаётся подключён. Заставка играет текущую сцену заново на новом языке, плеер, карточка, дерево слоёв и окна перерисовываются. Адрес страницы становится /<язык>/ (например /es/; у языка корня домена — просто /) — язык задаёт только адрес, параметра ?lang= нет; ссылку на сайт на нужном языке — https://space.bl2.net/<язык>/ (русский — https://space.bl2.ru/). |
| reset | — | Перед сценой: стоп облёта, взгляда, смены времени; Галактика выкл; метки, точки пролётов, окна медиа — прочь (в сценах заставки вставляется сам). |
| remember / recall | name | Запомнить состояние (камера, время, вид, интерфейс) / вернуть. |
| onstop | restore | По умолчанию включено: при конце, Esc, ✕ или уходе агента вернуть всё как до первой команды агента: слои и переключатели вида, панели и спрятанный интерфейс, камеру (ракурс, расстояние, центр, слежение), выбранное, время и скорость (или «вживую»); музыку и голос — стоп. Своё агента (sites, draw, mark, flybys, image, media, spawn) снимается всегда. restore: false (в файле сюжета — "onstop": "keep") — камеру, время и выбор оставить, как показал агент. У exec откат по концу — только при явном onstop {restore: true}. |
| console | open | Консоль >_ открыть / закрыть. |
| sites | body | code, group, ids, light, on | Места посадок: карточки у точек на поверхности (в сцене). body можно не писать — тело по группе (apollo, luna, rovers, new, farside, first, china — Луна; mars, chryse — Марс; venera — Венера) или по ids. Пример: sites {group: 'apollo', light: true} → {lat, lon, alt, at, ids}; затем time {at, rate: 0} и fly {name: 'Moon', lat, lon, alt}. Без перелёта по lat/lon камера может смотреть на обратную сторону — карточек не будет видно. light: true — время, когда места на дневной стороне (ответ at). |
| flybys | craft, on | Точки гравманёвров и выходов на орбиту на пути аппарата (из траектории миссии). |
| orbit | speed (°/с), turns | ms, pitch, dist, end, blend, ease, stop | Облёт тела. end: {to, deg, elev} — кончить ровно на этом ракурсе (в одну сторону, без возврата). Кончился на ходу — докатывается, следующий fly подхватывает скорость. |
| look | to, ms, ease, clear | Взгляд на объект: глаз на месте (или облётом), взгляд плавно поворачивается и следит. clear: true — обратно в центр. |
| select, shadow, camera target | name; name, by, center, ms, dist; target | Для сцен заставки: select {name} — путь тела ярко ('' — снять); shadow {name, by, center, ms, dist} — камера на пятне тени затмения; camera {target: {between: [a, b], k}, ms} — центр взгляда между телами (облёт — вокруг него), target: null — обратно; look {to: 'galcenter'}; fly {frame: {path, from, to, with}, elev, deg} — рамка на весь путь аппарата. |
| follow | name, ms, dist, ease | Следить за другим объектом: плавный переезд с тем же ракурсом. |
Показать маленькое тело (комету, межзвёздный объект — с десятков миллионов км его не видно): follow {name} или fly {name}, затем mark {name, label: true} — мигающий разноцветный маркер у тела (виден и при hideUi; ещё color, speed), select {name} — его путь ярко. Снять: mark {remove: name} или mark {clear: true}, select {name: ''}; при откате onstop и уходе агента маркеры снимаются сами.
Плавность ease — как timing function в CSS: linear, ease, ease-in, ease-out, ease-in-out, smooth, cubic-bezier(x1,y1,x2,y2) или формула от t (0…1): 1-pow(1-t,2), t*t*t*(t*(6*t-15)+10).
Внахлёст: {cmd, args, then: [шаги], before_end: мс | after_begin: мс | when: "условие"} — вложенные шаги идут одновременно с командой. Пример: облёт Земли, разворот на Ио, перелёт к ней:
[ {"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} ]
Песочница, графика, космонавт
| cmd | args | что |
|---|---|---|
| spawn | id, like, diameter, color, at: {near, dist, dir} | pos, vel | Свой объект: копия тела или глыба; встаёт на круговую орбиту вокруг главного тела (законы, не прямые) |
| move | id, vel | impact: {target, at} | Новая скорость или «попасть в тело к моменту» |
| despawn / objects | id | all | Убрать / список |
| draw | items | clear | remove | Графика в пространстве для отладки и уроков: arrow (vel, sun, north, {sum}), axes (крен/тангаж/рыскание), line, dash, path, angle (дуга и градусы), project (проекция на тело, высота), point, text; label — текст вдоль линии |
| flybys | craft, on | Точки гравманёвров на пути аппарата: «Тело · дата» и что дал манёвр; on: false — убрать |
| sites | body | code, group, ids, light, on | Места посадок (Луна, Марс, Венера, Титан): точки и карточки в сцене — фото, название, год, описание; group — совмещённый вид (apollo, luna, rovers, new, farside, first, china, mars, chryse, venera; тело — по группе, body не обязателен); light: true — места на дневной стороне; ответ — lat, lon, alt для fly; on: false — убрать |
| astro | action: goto | view | shield | land | exit, name, tpd, yaw, pitch, on, text | Космонавт: якорь на что угодно (тело, МКС, номер NORAD, астероид); view — вид на фигурку; shield — снять/поставить щит, своя надпись |
Консоль страницы — клавиша ё.
Как работать
- Прочитать эту страницу (
help),connectс кодом. - Спросить у страницы данные:
search/open(объекты),body(размеры, освещённая сторона),where(положения),find(события),layers,state. - По этим данным подготовить действия: один сценарий
play, или презентациюdeck, которую человек листает сам, или своиbuttons. - Запустить; проверить по
screenshotиlogs; поправить.
5. Пример: «расскажи мне про планеты»
connect {code}
layers → layer {key, visible:false} для всего, кроме oplanets
music {theme: 0, volume: 0.4}; hide_ui {on: true}
play {steps: [
{cmd:"fly", args:{name:"Марс"}, wait: 3800},
{cmd:"camera", args:{yaw: …, pitch: …, dist: …, ms: 3000}, wait: 3200}, // значения — из body {name:"Марс"}
{cmd:"say", args:{title:"Марс", sub:"228 млн км · свет идёт 13 минут", text:"…", audio:"mars.mp3"}, wait: "speech"}, // голос — Piper в браузере (ru ruslan, en amy); audio — готовый mp3 вместо него
…
]}
6. Ресурсы
- Тела: Солнце, 8 планет с лунами, карликовые планеты, ~1.4 млн астероидов (группами по размеру), кометы, межпланетные аппараты, ~30 000 спутников Земли (CelesTrak, раз в сутки), созвездия в объёме, модель Млечного Пути с туманностями и скоплениями, 44 000 галактик (2MRS).
- Имена — по-русски и по-английски (
Марс/Mars); координаты — экватор J2000, км. - Время — любая дата (вдали от сегодня эфемериды грубее; спутники — только ±90 дней от каталога).
- Ссылки на вид (
?o=…&d=…&v=…&t=…) открывают то же самое у любого.
Правила: страница принимает только эти команды; никаких переходов на другие сайты и личных данных. Управление — у человека: он может нажать «Отключить» в любой момент.