Egor Urvanov

Architecture of “Sibiria”: C4 and critical paths

C4 diagrams down to components and four critical paths as sequences: a frame, a night threat, a story step and saving.

“Sibiria” is a top-down taiga survival game in the browser: no engine, no build step, about fifty scripts drawing on a 2D canvas. This is how it is put together: C4 diagrams down to the component level and the four paths everything rests on.

C4: context and containers

Level 1. The system and its surroundings

Playermouse, keyboard, touch“Sibiria”a browser gameGoogle Fontsinterface fontsBrowserlocalStorageplaysloads fontssaves
The game has no server of its own: any static host will do. Without the fonts it still runs, only the typeface changes.

Level 2. Containers

index.htmlcanvas, HUD, stylesCore and datacore, data, content/*Systemsjs/*.js modulesPicturegfx, art-*, particlesSoundaudio.js, Web AudioInterface and loopui, input, mapStoragelocalStorageloads scriptsnumbers, storyupdate(dt)renderSound.framesave
The loop lives in the interface and calls the systems, the renderer and sound in turn. All scripts load in order through the tags in index.html.

C4 level 3: components of each context

Five contexts where the game is decided are shown. Saving is covered below as a critical path.

World

GenerationWorld.gen(seed)Zones8 zones, own rulesSnow depthdepth.jsIcefalling throughWindwind.jsSnow on thingssnow.jsLiving thingsblade, door, wiresResponsesinteract.jszone mapsurfaceriverblow, driftswayscapsevents
The world is built from a seed: forest, zones and drifts are recomputed, not stored.
  • Zones (aufeis, burnt forest, boulder field, bald peak and more) change the rules: cold, pace, hazards.
  • Responses are a table of “event + properties of the thing → reaction”, not “who with what” pairs.

Hero, survival and threats

Time and weathertime.jsHeromotion, skillsActionsE, F, craft, sleepSurvivalwarmth, hunger, healthFire and stovefire.jsThreat directortension, budgetWolvesscout, packRogue bearbear.jsFaunahares, deerfrostclothinggestureswoodheathero statespawnsnight
Warmth drops with frost and wind and rises at a fire or stove; the director watches the hero and decides when the wolves come.
  • The threat director works like Left 4 Dead: tension → budget → omen (howl, tracks) → scout or pack.
  • A storm drives the pack away, and at low health the director pauses.

Settlement and people

Datacontent/npcs, people, questsCharactersnpc.jsQuestsquests.jsConversationtalk.jsSettlementworkers, building, agesWorld barksbarks.jsInputright-click ordersCtx summarycontext.jsentriesentriestalksdialogue nodesordersurroundings
A new character or quest is an entry in the data; the executors stay as they are.
  • Settlers follow a “gather → store → again” loop; extra builders give diminishing returns.
  • Four ages, eight kinds of buildings, trade in roubles at a floating rate.

Story

Eventscontent/events.jsChapterschapterTickOperationsOPS, Story.runHelicopterheliTickDialogues, cardsUI.dialogCheckpointSaveGameEndingfinale.jsCompassgoalTargetevery stepwhen → doevery stepshownew chapterrescuedgoal
The story is data and Story only executes it: “when the condition holds, run the operations”.
  • Seven chapters, five endings: the branch depends on whether the helicopter comes and whether the settlement is ready.
  • Entering a chapter makes an autosave by itself.

Picture, sound and interface

Inputinput.jsMapmap.jsInterface and loopui.jsQualityauto-simplifyRenderergfx.jsSoundaudio.jsSpritesart-*.jsParticles, weatherparticles.jsintentopenrenderfpsSound.framedrawslayers
All input goes through one state machine, and the picture is built in layers with one light and one sun.
  • All sound is synthesised in Web Audio, no files; the music follows the danger.
  • If frames run past 28 ms or below 30 fps for two windows in a row, the graphics are simplified.

Four critical paths

1. One frame

BrowserLoopupdateRendererSoundQualityrequestAnimationFramedt ≤ 0.05 сupdate(dt)time, hero, fire, survival, threats, people, settlementworld updatedGFX.renderSound.framesample(fps)panel or dialogue: visual only
During sleep update runs four times per frame; with a panel open the world stands still and only “breathes”.

2. A night threat

SurvivalDirectorSound, toastsWolvesFirewarmth, hunger, healthtension → budgethowl and tracks (omen)spawnPack / spawnScoutfire protectionlunge: healthstorm, dawn or rout: the pack leaves
A warning first, then the attack: the player has time to reach a fire.

3. A story step

Story.tickEventsOperationsInterfaceChaptersSaveGamewhen(G, c)condition holdsrun(do)flags, itemsdialogue, toastchapterTickall goals done?chapter cardcheckpoint()
An event fires once, tracked by a saved flag; the chapter changes when all its goals are done.

4. Saving and loading

PlayerGame, UISaveGameSavesWorldsleep in the hut, new chaptercheckpoint()snapshot: seed + changeswrite("auto")localStorage“Continue”load(json)gen(seed)changes applied
Only what changed is saved: forest, zones and drifts are recomputed from the seed.

The rules everything rests on

  • One state G. Systems read and write it; a save is G plus the seed.
  • The world is computed from a seed. Forest, zones and drifts are not stored.
  • The story is data. Chapters, events, quests and people sit in content/; the executors know nothing about them.
  • One step order. update calls the systems in a fixed order.
  • One Ctx summary. Barks, sound and light take “what is around the hero” from it.

Weak spots

Where What is wrong
Script order About 55 tags in index.html; the order matters and nothing checks it.
Global modules Modules call each other by name; the code has no boundaries between contexts.
The big G One mutable state for everything: any system can break another’s field.
ui.js One thousand-line file holds the HUD, dialogues, map, frame loop and saves.
Old saves Saves before version 4 are not readable: the world is different.

About the game

Game pageSibiriaSurvive four nights in the Evenki taiga, 1993 — then build a settlement around your hut
© 2026 Egor Urvanov