ForestBook
An interactive picture book about forest animals — listen, look closer, turn the page.
Lesní kniha
Prohlížecí hra v podobě knihy. Na levé straně je vždycky kus lesa s jedním zvířetem, na pravé zápisník, který se doplňuje podle toho, co ve scéně najdeš. Listuje se šipkami, klávesami ← →, nebo kliknutím na kapitolu dole.
Spuštění
Číst se dá bez čehokoli: otevři index.html v prohlížeči, klidně
dvojklikem z disku. Je to čisté HTML, CSS a JavaScript bez závislostí a data jsou
v .js souboru (ne v JSON) právě proto, aby to fungovalo i z file://.
Upravovat knihu se dá v administraci, a ta potřebuje běžící server, protože na rozdíl od prohlížeče umí zapisovat na disk:
node server.js
Pak je kniha na http://localhost:5180 a administrace na
http://localhost:5180/admin.html. Server poslouchá jen na 127.0.0.1.
Nasazení
Kniha běží veřejně na https://forest-book.tomlabs.cz. Na server jde jen čtení:
tentýž server.js s NODE_ENV=production vydává statiku, administrace
(admin.html, js/admin.js) v obrazu vůbec není a zapisující API vrací 404.
Obrázky a zvuky se vydávají s týdenní cache, stránka a data se ověřují při každém
načtení.
Verze aplikace je v package.json; server ji ukazuje v hlavičce knihy
(/api/verze-aplikace) a na https://forest-book.tomlabs.cz/health:
{ "status": "Healthy", "version": "1.1.0", "name": "forestbook" }
Před vydáním verzi zvedni, ať je na první pohled vidět, co kde běží.
Nasazuje Azure DevOps pipeline (azure-pipelines.yml) při
každém pushi do main: zkontroluje syntaxi, sestaví Docker obraz, ověří, že
/health odpovídá a administrace je pryč, a pak přes SSH nahraje release na VPS,
spustí docker compose, nasadí Nginx konfiguraci a certifikát a nakonec zkontroluje
veřejný /health. Pipeline čeká variable group forestbook-deployment se secret
proměnnou VPS_SSH_PRIVATE_KEY_B64 (nebo VPS_SSH_PRIVATE_KEY).
Cíl:
- doména:
forest-book.tomlabs.cz - VPS:
37.205.12.163, uživatelcodexdeploy, cesta/opt/forestbook - kontejner
forestbookna127.0.0.1:5311, před ním Nginx (nginx.forest-book.tomlabs.cz.conf)
Ruční zkouška stejného obrazu:
docker build -t forestbook:local . && docker run --rm -p 127.0.0.1:5311:5311 forestbook:local
Hloubka (paralaxa)
Stránka si paralaxu zapne polem hloubka: true (nebo hloubka: { x, y }, kde bod je rovina ostrosti) (zatím jen liška; ostatní zůstávají beze změny). Ke každé její ilustraci pak patří hloubková mapa v assets/hloubka/<id>/<soubor>.depth.webp (světlé = blízko).
js/hloubka.js podle ní v shaderu posouvá pixely při pohybu myši, náklonu telefonu a v klidu
pomalým houpáním — popředí se hýbe víc než pozadí. Rovina ostrosti leží u oka zvířete (první mrk,
pak loutka, nakonec dech stránky), takže se zvíře, víčka a body u něj nehýbou; ostatní body jedou
s tím, co označují. Mapa se hledá podle názvu; bez mapy, bez WebGL, ze
file:// a při prefers-reduced-motion zůstane obrázek klidný. Dech zvířete se při paralaxe počítá přímo v shaderu (statická kopie .al-breath by se přes
posouvaný obraz rozdvojila, proto se skryje) a víčka se posouvají s hloubkou oka. Všechny scény
sdílejí jeden WebGL kontext.
Mapy se vyrábějí lokálně skriptem tools/make-depth.mjs (model Depth Anything
small, cd tools && npm i --no-save @huggingface/transformers sharp && node make-depth.mjs, po výměně ilustrace znovu) a commitují se, server nic nepočítá.
Mapy leží mimo složky stránek schválně, aby se nenabízely ve výběrech souborů v administraci.
Logy
Server loguje do denních souborů forestbook-RRRR-MM-DD.log ve složce LOG_DIR (výchozí
./logs, v kontejneru /logs = /var/log/forestbook na VPS) a drží je LOG_RETENTION_DAYS
(14) dní. Úroveň určuje LOG_LEVEL, přepisy podle zdroje LOG_LEVELS=http=debug.
Kontrola /health se loguje jen na úrovni trace; do logu se nedostane query string
a hodnoty vypadající jako token či heslo se zamaskují.
Stránka Logy (/logs.html, odkaz v administraci) ukazuje posledních tisíc událostí
živě, s filtry a se stahováním souborů. Lokálně je volně přístupná; na serveru neexistuje,
dokud se nenastaví LOGS_TOKEN — pak se token zadá na stránce.
Analytika
GA4 se načítá přes tomlabs-consent.js (kopie z TomLabs.Consent, aktualizuje se
Sync-Consent.ps1) a až po kliknutí na Přijmout. Měřicí ID se do index.html dosazuje
za běhu z GA_MEASUREMENT_ID (pipeline, skupina web-analytics); bez ID není banner ani tag.
Jak je to poskládané
index.html kniha
admin.html administrace
logger.js logování (soubory + paměť pro stránku Logy)
logs.html, js/logs.js stránka Logy
server.js lokální server (statika + zápis dat a nahrávání souborů)
css/style.css vzhled knihy; barvy a rozměry jsou nahoře v proměnných
css/pohyb.css částice, dech, mrkání a loutky — načítá kniha i administrace
css/admin.css vzhled administrace
js/book.js engine knihy
js/admin.js editor
data/pages.js OBSAH — celá kniha v jednom objektu
data/zalohy/ předchozí verze dat, zakládá je server při ukládání
assets/stranky/<id>/ všechno, co patří jedné stránce — scéna, detaily,
zvíře na průhledném pozadí, hlas i zvuky bodů
assets/spolecne/ co patří celé knize — atmosféry ročních dob
assets/CREDITS.md odkud zvuky jsou a pod jakou licencí — musí jít s knihou
Postup (co už jsi objevil), zvolená roční doba i vypnutý zvuk se ukládají do
localStorage prohlížeče. Tlačítko Začít znovu smaže postup.
Zavřená kniha
Kniha se otevře až kliknutím na desku — plátěná deska s titulem v rámečku, která se odklopí kolem hřbetu a odhalí za sebou dvojstranu. Má to i praktický důvod: prohlížeč nepustí zvuk, dokud uživatel na stránku neklikne, a tohle kliknutí přijde přesně ve chvíli, kdy se kniha rozevírá — atmosféra tak naskočí se správným načasováním, ne až o pár listů dál.
Šipka vlevo na první stránce knihu zase zavře, stejně tak Začít znovu. Deska se nastavuje v administraci v režimu Kniha: titul, podtitul, pobídka a nepovinný obrázek. Bez obrázku je to jen titul v rámečku na plátně.
Roční doby a část dne
Kniha zná čtyři období a den/noc, tedy osm variant. Přepínač je dole pod ilustrací na každé stránce, ale doba je vlastnost čtení, ne stránky — přepneš zimu a zimní je celá kniha. Bez uložené volby se kniha otevře v tom, co je zrovna venku (podle data a hodiny v počítači).
Varianta smí přebít cokoli: ilustraci, popisek, zvuk, texty i polohu bodů.
varianty: {
"zima-den": {
art: { src: "assets/scenes/liska-zima.webp", ratio: 0.6667 },
zvuk: { ambient: "assets/sounds/les-zima.mp3" },
hotspots: { nora: { x: 12, y: 66 } }, // jen posun, zbytek zůstává
},
},
Když varianta vlastní ilustraci nemá, engine vezme základní obrázek a nasadí
na něj tónování z tonovani v datech: barevný filtr, přeliv a částice té doby —
v zimě sníh, v noci tmu a můry. Je to náhrada, ne zázrak: zimní tónování udělá
z podzimní mýtiny studenou podzimní mýtinu, sníh na zemi nedomaluje. Jakmile
v administraci nahraješ skutečnou zimní scénu, tónování se u té varianty
přeskočí.
Tónování se nasazuje na všechny malované vrstvy naráz — na scénu, na loutku i na dýchací kopii. Kdyby se nasadilo jen na scénu, zvíře by v noci svítilo.
Popisek scény se skládá sám z pole misto a aktuální doby („Bučina · zimní
den“), takže nemůže zůstat viset na jedné roční době. Varianta ho smí přebít
polem caption.
Administrace
Editor umí to, kvůli čemu vznikl: posouvat body a zvíře tažením myší přímo v náhledu scény, měnit texty, přiřazovat a nahrávat obrázky i zvuky, a to zvlášť pro každou z osmi variant.
Vlevo je seznam stránek, nahoře přepínač variant (zeleně orámované jsou ty, které už mají vlastní ilustraci), uprostřed náhled a vpravo panel. Panel má tři režimy, protože jsou to různé práce a nemají si zaclánět: co v daném režimu nejde chytit, v náhledu jen tiše svítí.
Obsah — o čem stránka je:
- Stránka — jméno, latinsky, místo, úvodní odstavec, základní ilustrace, hlas zvířete. Poměr stran si editor dopočítá sám z nahraného souboru.
Ve všech výběrech souborů se nabízí jen složka téhle stránky a k tomu to
společné, takže se do nabídky nepletou ostatní zvířata. Nahrané soubory míří do
složky stránky; v režimu Kniha do assets/spolecne/.
Obrázky se vybírají z dlaždic s náhledy, ne z rozbalovátka — soubory
z generátoru se jmenují call_2Qlf6h2BjPSL… a z názvu se nepozná vůbec nic.
Pod náhledem scény je navíc napsáno, odkud obrázek pochází: jestli je to
základní ilustrace tónovaná do zvolené doby, nebo vlastní obrázek varianty.
Bez toho vypadá změna základní ilustrace jako porucha — varianta má přednost,
takže se v náhledu nic nezmění.
- Varianta — ilustrace, atmosféra a popisek pro právě zvolenou dobu.
- Body ve scéně — seznam, přidávání, mazání, texty, obrázek detailu, zvuk a přesné souřadnice.
Pohyb — co se na stránce hýbe. Je to prostý seznam vrstev, klidně prázdný
nebo o třech položkách; stránka může být jen s rostlinou nebo úplně bez pohybu.
Mechanismy nejsou zvířecí, jen se tak jmenují: dech je „vezmi kus obrázku
a nech ho tiše pulzovat“, což na kapradí vypadá jako vlnění ve větru, a loutka
s pohybem houpani a osou u země je rostlina v průvanu.
Ve formuláři je jen to, co je věcí vkusu — rychlost a síla. Kde věc leží a jak je velká, se nastavuje v náhledu tažením: dech má přerušovanou elipsu s úchytem na velikost, víčko je vidět ve skutečné velikosti i barvě a má rohový úchyt. Přesná čísla jsou schovaná pod Přesná čísla, kdyby bylo potřeba sáhnout ručně.
Vedle každého výběru zvuku je ▶, takže se dá poslechnout, aniž by se otvírala kniha. Hraje vždycky jen jeden; druhý klik ho zastaví.
V režimu pohybu je ▶ Přehrát pohyb. Náhled se přepne do živého: postaví se v něm přesně ty vrstvy, které staví kniha, se stejnými třídami z css/pohyb.css — zvíře se rozdýchá, mrkne a klove, a přidají se i částice roční doby. Není to napodobenina, je to tentýž kód; proto je pohyb v jednom souboru, který načítá kniha i administrace, a ne dvakrát opsaný. Během přehrávání se nedá nic chytit, na posouvání je potřeba zastavit.
Dvě věci umí editor změřit z obrázku sám:
- Osa otáčení loutky — z průhledného okraje PNG se zjistí, kde v něm zvíře doopravdy je, a osa se položí k jeho patě. Při výměně obrázku se navíc dopočítá šířka tak, aby zvíře zůstalo na scéně stejně velké, i když má nový soubor jinak široký průhledný okraj.
- Barva víčka — nabere se z prstence kolem oka (bez tmavého okraje a odlesků). Kniha ji ale
za běhu bere znovu z obrázku, který se právě zobrazuje (
js/oko.js), takže uložená barva je jen záloha profile://a nemusí se ladit pro každou roční dobu zvlášť. Ladit se musí poloha očí: každá varianta je samostatný obrázek a zvíře v něm může být o kousek jinde (upodzim-nocje liška o 0,7 % níž), proto má taková varianta vlastníalive.
Kniha — nastavení, které neplatí pro jednu stránku, ale pro celou knihu:
- Deska zavřené knihy — titul, podtitul, pobídka a nepovinný obrázek.
- Atmosféry podle doby — ke každé z osmi variant se přiřadí zvuk. Tenhle se pustí na každé stránce, která si atmosféru nepřebíjí vlastní; proto je u varianty v režimu Obsah napsáno, co se zdědí („zděděno: les-noc.mp3“), a ▶ vedle toho pole přehraje právě ten zděděný soubor.
- Tónování scén — pro každé období a pro den i noc filtr, přeliv, míchání a zaškrtávátka částic. Náhled v tomhle režimu běží živě, takže je změna hned vidět.
Automatické hledání oka jsem zkoušel a zahodil: proti pěti ručně změřeným očím obstálo jen někde (liška na desetinu procenta, veverka o šest procent vedle). Malované zvíře má kolem hlavy tmavé srsti dost a oko z ní nevyčnívá spolehlivě. Posadit víčko okem, když je vidět v náhledu, je rychlejší i jistější.
Tlačítko Uložit knihu zapíše data/pages.js a předchozí verzi odloží do
data/zalohy/. Server data před zápisem zkusí spustit; když jsou rozbitá,
odmítne je uložit.
Dvě otevřené administrace najednou jsou past: každá drží v paměti celou knihu a uložení zapisuje všechno, takže ta druhá by potichu vrátila práci té první. Server proto u dat vede razítko — administrace si ho pamatuje od načtení a posílá zpátky; když se soubor mezitím změnil, uložení se odmítne a je potřeba načíst stránku znovu. Rozdělanou práci to nezachrání, ale aspoň se nic nepřepíše bez vědomí.
Jedna daň: uložení z administrace nezachová komentáře v data/pages.js —
soubor se generuje ze samotného objektu. Popis struktury je proto tady v README,
ne v datech.
Přidání nové stránky
Buď tlačítkem + nová stránka v administraci, nebo ručně do pole pages:
{
id: "jezevec", // musí být unikátní, ukládá se podle něj postup
type: "spread",
name: "Jezevec lesní",
latin: "Meles meles",
misto: "Smíšený les", // do popisku scény
intro: "Odstavec, který je vidět hned po otevření stránky.",
art: { src: "assets/scenes/jezevec.webp", ratio: 0.6667 },
hotspots: [
{
id: "hrad",
x: 30, y: 62, // procenta ILUSTRACE, ne stránky
label: "Jezevčí hrad", // popisek při najetí myší
short: "Věta nebo dvě, které se objeví po kliknutí.",
more: "Delší povídání pro zvědavé, schované pod tlačítkem „Víc o tom“.",
},
],
}
ratio je poměr šířky ku výšce ilustrace. Engine podle něj vsadí obrázek do
papíru jako desku, celý a bez ořezu. Obálka jde přes celou stranu na spad — to
je rozdíl mezi type: "cover" a type: "spread", dá se vynutit i ručně přes
art.fit: "cover" nebo "contain".
Průhledy dovnitř
Klik na bod ve scéně (nebo na už odemčenou poznámku) otevře desku s detailem: řez norou i s liščaty, vnitřek veverčího hnízda, ohryzaná šiška zblízka. Kartou se dá listovat šipkami ← → nebo tlačítky Předchozí / Další, zavírá se klávesou Esc, křížkem, nebo kliknutím vedle. Projít všechny průhledy zároveň odemkne všechny poznámky na stránce.
Každá stránka má vlastní složku, takže obrázek detailu si engine najde sám podle id bodu:
assets/stranky/<id stránky>/<id bodu>.webp např. assets/stranky/liska/nora.webp
Do dat se tedy nic psát nemusí. Jinou cestu vynutíš přes detail: "assets/…",
detail: false detail vypne a klik jen odemkne poznámku.
Detaily nemusí být čtvercové — rám v kartě se přizpůsobí poměru stran obrázku, takže se nic neořezává. U obrázků na výšku se sloupec sám zúží, aby karta vyšla na obrazovku.
Pohyb ve scéně
Nad ilustrací leží vrstva částic. Které to jsou, řídí roční doba podle
tonovani v datech — jaro a léto mají paprsky a prach, podzim mlhu a padající
listí, zima sníh, noc můry. Je to čisté CSS, žádné další obrázky ani knihovny.
Stránka smí přidat vlastní částice přes ambientNavic (tak má puštík záři
měsíce přesně tam, kde je měsíc namalovaný), nebo si celý seznam vzít do své
režie přes ambient.
| efekt | co dělá |
|---|---|
mlha |
světlé pruhy putující pomalu napříč scénou |
listi |
padající listí, každý list jinou rychlostí a v jiné rotaci |
snih |
pomalu padající sníh |
paprsky |
sluneční pruhy, které se jen prolínají |
pyl |
drobný prach stoupající ve světle |
mury |
noční hmyz poletující kolem |
zar |
světlo, které dýchá — měsíc, okno, cokoli svítí |
Řetězec vezme výchozí nastavení, objekt umí přebít počet (n), pozici (x, y
v procentech ilustrace) a velikost (s). Kolik čeho a jak dlouho je v datech
v castice, vzhled a dráhy v css/pohyb.css; nový efekt se
přidá na obou místech.
Kdo má v systému zapnuté omezení animací (prefers-reduced-motion), dostane
knihu v klidu — vrstva se vůbec nevykreslí.
Živá zvířata
Ilustrace jsou rastrové, takže zvíře nejde rozhýbat jako loutku. Dvě věci ale jdou i nad hotovým obrázkem:
alive: [
{ fx: "dech", x: 55, y: 65, rx: 21, amp: 2.8, d: 4.6 },
{ fx: "mrk", x: 57.0, y: 53.4, w: 4.4, h: 1.7, rot: -4, c: "#a84f0b", d: 7.4 },
],
dech položí přes scénu druhou kopii téhož obrázku, měkce vymaskovanou
kolem těla, a tu skoro neznatelně zvětšuje a zmenšuje. Protože je maska
rozostřená, není vidět žádný šev — jen se zvíře nadechne. mrk nechá přes
oko sjet víčko v barvě srsti; obě oči dostanou stejné d, takže zvíře mrkne
oběma najednou.
Souřadnice očí i barvy víček se dají vyčíst přímo z obrázku (najít tmavé shluky v okolí hlavy a vzít barvu pixelu nad okem). Když vyměníš ilustraci, je potřeba je přeměřit.
Loutka: zvíře jako samostatná vrstva
Když se má zvíře hýbat celé, musí být od scény oddělené — tak jsou udělané
stránky strakapouda a datla (dnes už z knihy vyřazené): scena.webp byl prázdný
kmen bez ptáka, zvire.webp datel s průhledným pozadím.
alive: [
{
fx: "loutka",
src: "assets/stranky/<id>/zvire.webp",
x: 30.2, y: 44.9, w: 72.4, // střed a šířka obrázku v % ilustrace
pivot: "56% 70%", // kolem čeho se otáčí — tady nohy a ocas
anim: "klovani", // klovani | houpani | klid
a: 7, // výchylka ve stupních
d: 5.2, // délka jednoho cyklu
},
],
w je šířka celého čtvercového PNG včetně průhledného okraje, ne šířka
zvířete — proto je to číslo větší, než by člověk čekal. Pod loutku engine
přidává měkký stín, aby na scéně neplavala; vypne se stin: false.
Průhledné pozadí jde vygenerovat rovnou, stačí si o ně říct v promptu. Chce to ale trvat na kompozici: u strakapouda měly první dva pokusy ptáka oříznutého o okraj plátna a utrženou nohu vedle těla. Zabralo až zadání ve smyslu „malá figura uprostřed velkého prázdného plátna, ze všech stran široký okraj, nic se nedotýká kraje“.
Posazení loutky se nedá odhadnout od oka. Postup, který funguje: z alfa kanálu
PNG se změří, kde přesně v něm zvíře leží, z toho se dopočítá w tak, aby mělo
na scéně kýženou výšku, a x/y se posunou o odchylku středu zvířete od středu
plátna. V administraci to jde i tažením.
Zvuk
Dvě vrstvy. Atmosféra je smyčka, která patří k roční době (atmosfery
v datech, klíč varianty → soubor) a při otočení listu i při přepnutí doby se
prolne s další; stránka nebo varianta si ji smí přebít vlastní. Jednorázové
zvuky patří ke zvířeti (tlačítko u jeho jména) nebo k bodu ve scéně (zahraje
se, když ho objevíš).
zvuk: { hlas: "assets/stranky/liska/hlas.mp3" }, // na stránce
{ id: "bubnovani", zvuk: "assets/sounds/…mp3", ... } // u bodu
Prohlížeč nepustí zvuk sám od sebe, dokud uživatel na stránku neklikne —
atmosféra proto čeká na první interakci a pak naskočí prolnutím. Prolínání jede
na časovači, ne na requestAnimationFrame: ten se v neaktivní záložce zastaví
a hlasitost by zamrzla na nule. Chybějící soubor nic nerozbije, prostě je
ticho.
V liště je vypínač; volba se pamatuje. Když má člověk zvuk vypnutý a klikne přímo na hlas zvířete, bere se to jako projev vůle a zvuk se zapne.
Zvuky jsou z Wikimedia Commons (nahrávky z archivu Xeno-canto, CC BY-SA 4.0) a z archivu BBC Sound Effects (licence RemArc). Autoři a licence jsou v assets/CREDITS.md — kdyby se kniha měla zveřejnit, dva zvuky z BBC se musí nahradit, jejich licence pokrývá jen osobní a studijní použití.
Výměna ilustrací
Složka stránky se jmenuje podle jejího id. Když se id změní, složka se
nepřejmenuje sama — je potřeba ji přejmenovat ručně, jinak si engine detaily
nenajde.
Soubory v assets/ klidně přepiš vlastními kresbami; formát je jedno (PNG, JPG,
WEBP i SVG). Souřadnice bodů se pak budou hodit posunout — na to je
administrace.
Současné ilustrace jsou vygenerované jedním společným stylovým popisem (kvaš a akvarel, teplé přírodní barvy, bez textu), aby kniha držela pohromadě: scény na výšku 1024×1536, detaily 1024×1024 nebo 1024×1536.
Co zatím chybí
- Skutečné ilustrace pro jednotlivá období. Zatím se všechno tónuje z jedné scény na stránku; osm variant na stránku je osm obrázků.
- Sezónní texty. Poznámky jsou stejné v zimě i v létě, i když třeba u lišky by se v zimě hodilo psát o myškování ve sněhu.
- V knize jsou zatím liška a veverka (a deska); další zvířata se přidávají po jednom. Loutka zatím žádnou stránku nemá. Promptové podklady: docs/prompts-squirrel.md.
- Loutka se hýbe jako jeden tuhý kus — neohne se jí krk ani křídlo.
- Vyhledávání nebo rejstřík pojmů napříč stránkami.
Changelog
Changelog
1.1.0 — 2026-10-01
- Added: application log — daily rolling files (14 days kept) plus a live Logs page (
/logs.html) with level, source and text filters, file tail and download. Locally it is open; on the server it exists only whenLOGS_TOKENis set. - Added: Google Analytics 4 behind a cookie consent banner. Nothing is sent before the visitor accepts; a "Cookies" link in the header reopens the choice.
- Added: application icon and favicon.
- Added: listing in the TomLabs app store.
- Changed: the book is restarted with the fox only; the other animals (tawny owl, squirrel, great spotted woodpecker, black woodpecker) were removed and are added back one by one, starting with the squirrel.
- Added: the squirrel page — eight illustrations (base plus every season and time of day), five detail pictures and five hotspots, with depth parallax.
- Changed: phone layout — the illustration fills the whole screen, the notebook is a bottom sheet (tap or swipe up to open, tap the picture to close), the season switcher floats under the header, the picture can be dragged sideways so every hotspot is reachable, and iOS asks once for tilt access for the parallax.
- Added: depth parallax — the fox page has depth maps for all its illustrations (
assets/hloubka/) and the scene shifts with the mouse, the phone tilt and, when idle, a slow sway: foreground moves more than the trees in the distance. The animal's eye stays the focal plane, so blinking and breathing stay in place. Without WebGL, a depth map or underprefers-reduced-motionthe picture stays still. - Changed: the fox no longer blinks or breathes — it conflicted with the parallax; the engine still supports both for other animals.
- Fixed: the fox blinks on her eyes in every season and time of day. The eyelid colour is now sampled from the illustration that is actually shown (it used to be one hand-tuned colour that glowed orange on the night scenes), the autumn-night picture has its own eye positions, and the blink is faster with a dark lash line.
- Fixed: the administration's eyelid colour picker samples a ring around the eye instead of the dark brow above it.
1.0.0 — 2026-09-30
- Added: the interactive forest book — spreads with an animal, a notebook that fills in as you discover things, seasons and day/night, sounds.
- Added: local administration for editing pages, hotspots, motion and sounds.
- Added: public deployment with a
/healthendpoint.