reference
Changelog
Every published release and the unreleased changes on main.
⌖ Explore the library in 3D → · View as Markdown
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.16.0 - 2026-10-24
Added
Rift, a pair of linked rifts for walkable shortcuts within one world. Each end is a ragged, breathing tear in the air with an iridescent pink, violet and cyan rim that sheds glitter, and shows the other end live with real parallax. Walk into either and you come out of the other, momentum and all. Set the far end withdestination. Withactive={false}both rest as small shimmering bubbles. For travel to another world or page,Portalis still the one to use.Grasstakes a height preset:height="short"(a lawn),"medium"(the default, unchanged),"tall"(a meadow) or"very-tall"(reeds up to your chest). A number in units still works. Taller grass grows thicker blades that lean a little, so it doesn’t read as needles.- Keyboard navigation in
<WorldEditor>and the workshop’s Build view: holdWASDto pan across the ground (Q/Efor down and up) and the arrow keys to orbit, with⇧to go faster. With a node selected,WASDQEstep it along the world axis nearest the camera instead (0.1 m, or one snap step in the workshop;⇧for ten), and a burst of steps undoes as one.@runek/coreexports the camera half asuseOrbitKeysfor your own orbit views. - The registry is versioned:
/r/<version>/serves each release’s components, and/r/versions.jsonlists them.runek add(andlist,info,validate,skill) now read the snapshot that matches your project’s@runek/core, so the source you pull only uses core APIs you have.--core <version|latest>picks another one;--core latestalso upgrades@runek/core. A registry withoutversions.json(a local checkout, a fork) works as before.
Changed
- Breaking:
rng(seed)scrambles the seed before its first step, so every seeded component looks different for the same seed after you upgrade. Re-pick any seed you chose by eye. Before, the first value drawn for a small seed was close to 0 (everyPersonseed below 100 came out with the same gender), andrng(0)returned 0 forever. Same seed, same result still holds within a version. - Duplicate moved from
dto⌘Din<WorldEditor>and the workshop, and the workshop’s World panel fromwto,, to make room for WASD. Player’s first-person view is much wider and softer at the edges, closer to how people see. It shows 140° across on a 16:9 screen (was about 90°) through a gently curved lens, so the room wraps around you without the tunnel stretch a flat camera gives at that width. Like most games it follows the screen’s shape: an ultrawide sees more across (about 160° at 21:9, up to 200°), a narrower screen less, down to 110°, and a phone held upright shows a narrower slice of that. Focus falls off smoothly with the angle off straight ahead, so turning toward something brings it into focus. Tune them with the newfovandperipheralBlurprops (peripheralBlur={0}turns the blur off). Clicks and hover labels follow the lens. Third-person and overhead views are unchanged.@runek/coreexportsuseLensPositionfor your own<Html>labels.Portalis now a swirling vortex around a lightless core instead of a spinning ring: spiral arms, a bright photon rim, drifting motes that fall inward, a soft halo, and light pooled on the ground beneath it. It faces the camera from any side, andseednow shapes the swirl and the motes.Palmgrows a full, feathery crown instead of a handful of drooping ribbons: each frond is an arching midrib lined with leaflets, the young fronds stand up at the top, and the older ones arch out and hang lower, turning slightly yellow.frondsnow defaults to 16 (was 11) andfrondLengthto 2.8 (was 2.2), so an existing seed grows a different crown.Personturns its head toward the player from 5 units away instead of 9, so figures across a room no longer all stare at you. SetlookRadiusto change it.BookandBookshelfbooks are only hoverable and clickable once the player’s avatar is within reach (3 units from its eyes), so a shelf across the room can’t be opened from where you stand. Tune it with the newreachprop. Without aPlayer(an orbit camera, the editor) everything stays clickable.@runek/coreexports the check asuseReachfor your own clickable meshes.
Fixed
runek addno longer installs components that need a newer@runek/corethan npm has. The registry at/rserved source from the unreleased main branch, sorunek add bookshelfwith@runek/core0.15.0 failed to typecheck (useReachandDEFAULT_REACHmissing)./rnow serves the latest release, which also fixes older CLIs. (#21)<WorldEditor>’s props panel keeps up with the world: it shows where a node is after a gizmo drag, a nudge, or an undo instead of the numbers it opened with, and editing one field no longer snaps the node’s other fields (or earlier edits) back to those stale values.- The workshop inspector’s number fields keep a value stepped with the arrow keys (it reverted on blur), show up to three decimals instead of rounding to the step (so a y of 0.002 no longer reads as 0), and no longer rewrite a value just because you clicked in and out.
- The workshop’s wall sign no longer draws its “build · tune · share” line over the WORKSHOP lettering.
- The camera no longer shakes while walking, most visibly in first person
and on high refresh rate displays.
<World>now steps physics once per rendered frame (splitting long frames into short sub-steps), in step with the avatar controller, instead of on a fixed 1/60 s clock. - A custom
Playerbody (aPersonfrom the workshop’s “use as play avatar”, say) stands on the ground instead of hovering about 26 cm above it. The controller floats its capsule on a spring, andPlayernow lowers the body (and the default capsule) by that float, at any frame rate. Bodies placed as documented, feet aty = -0.65, need no change. If you lowered yours by hand to make up for it, put it back.
0.15.0 - 2026-10-18
Added
- The workshop’s avatar studio: a room behind the hall’s west door for
customising a
Person. Open it from the inspector’s customize, the doorway, the Studio mode, or4. The figure stands on a turntable while the Avatar panel edits it tab by tab (looks, body, face, hair, clothes, hat and extras, pose) with picture tiles, color swatches, and sliders, and the camera frames the part you are on. Clothes are a drag-to-reorder layer stack that shows what tucks in. Each trait shows whether you set it or it came from the skin, the kind, the seed, or a default, with reset, pin, and a per-tab shuffle. A 3 × 3 seed grid, look export and import (.look.json), and “use as play avatar” round it out. resolvePerson(props)reports what each of aPerson’s traits resolves to and where it came from (prop,skin,kind,seed, ordefault), without building any geometry.PERSON_COLORSholds the curated skin, hair, and eye colors a seed rolls from.- Lights:
TikiTorch(a bamboo torch with a flickering flame),CeilingLight(flush, pendant, or spot),Chandelier(one or two rings of arms, instanced, one shared light),DeskLamp(a banker’s lamp), andPictureLight(over a picture or on a bookshelf). Each takesonandnightOnly, which lights it only while the world’s sun is down. Tableware: a plate, bowl, glass, cup, or water jug, or a whole placesetting, with a drinkfillline and stacking plates and bowls.GameConsolewith up to four controllers,TableTennisat regulation size, andChessBoard, set from a FEN position or played out from the seed, on a table or its own pedestal (chessPiecesgives the placement without rendering).Screen, a display of anysizeon a wall, a stand, a desk foot, or in a frame, showing a seeded picture or desktop, a text banner, animated static, or a color.Poster, a wall print of seeded flat art with an optional caption and frame.- A code-only
textureprop onScreen,Poster,Tv, andMonitor: hand it a texture your app owns (a video, a canvas, a render target) to show instead of the built-in content. The components still load nothing themselves, and the prop schema lists it as code only. ArcadeCabinetgame:invaders(the default, unchanged),blocks,maze,snake, orpaddle, each laid out from the seed.useSun()in@runek/core: the sun for the world’s time of day, for components that react to day and night.SkyandLightRignow use it.isCodeOnly(prop)in@runek/core/data, and aninstanceprop type in the prop schema for live three.js objects such as aTexture.Staircaseform:solid(the default, unchanged) orfloating, thin treads on two sloped stringers, open underneath, for a flight that spans a void such as an atrium, a loft, or a mezzanine.- The docs site’s navbar shows the current Runek version in a pill next to the title.
Changed
- The library world’s component gallery is now a three-floor hall around an open atrium instead of one long corridor. Each floor holds a category (Grounds, Structures, Interiors), exhibits run alphabetically along the walls with a label where each section starts, and two floating flights cross the atrium between floors. A directory on the ground floor shows what each floor holds. The hall widens on its own as the catalog grows.
- Gallery miniatures live in their own paused physics world, so an
exhibit’s colliders can no longer block or push visitors (a
TerrainorShoreminiature used to leave an invisible full-size floor over the walkway).
0.14.0 - 2026-10-07
Added
- Agent-friendly docs:
/llms.txtindexes every guide and component,/llms-full.txtis the whole docs set in one file, every docs page is also served as Markdown at/docs/<slug>.md, and a new “Runek for agents” guide walks through discover, install, compose, validate, and preview. Component pages now list every prop with its type, default, and description. - A machine-readable catalog:
/r/props.json(every component’s props, types, defaults, and docs, generated from the TypeScript byjust registry) and/r/world.schema.json, a JSON Schema for world files. Add"$schema": "https://runek.nullorder.org/r/world.schema.json"to a world for completion and checks in your editor. The$schemaURLs thatregistry.jsonandrunek.config.jsonalready point at (/registry/schema.json,/registry/config-schema.json) now resolve. runek validate <world.json>: checks a world against the catalog (unknown components, misspelled props, wrong value types, bad anchors, duplicate ids) with did-you-mean suggestions. Components you wrote yourself are recognized from your install directory.runek info <name>: a component’s props (type, default, docs), dependencies, and a world-node and JSX example.runek preview <world.json>: prints a workshop link that opens the world in the browser.runek skill: installs a Runek agent skill into your project (.claude/skills/runek/SKILL.mdby default).--jsonoutput forrunek list,runek check-world, andrunek validate.runek --version(-v, orrunek version) prints the CLI version: a Pixelspace banner in a terminal, the bare version when piped.validateWorld,suggest,matches,describeType, and the prop schema types (SchemaMap,ComponentSchema,PropSchema) are exported from@runek/coreand@runek/core/data, so the same world checks run in the workshop, the CLI, and your own tooling.- A world file may carry a
$schemakey;serializeWorldkeeps it. - Every component now accepts
seed, as the contract requires. The 25 that had none (Bench,Chair,Table,Wall,Door,Window,Roof,Floor,Staircase,Arch,Pillar,Barrel,Bed,Shelf,Clock,Lamp,Sign,Well,Fountain,Lake,Shore,Ocean,Sky,LightRig,Player) accept it with no seeded variation, so nothing renders differently. The prop schema marks each componentseededor not, and the workshop only offers seed controls where a seed changes something. - Component pages show the component’s source in a collapsible panel.
- The workshop: an in-browser workbench at
/workshopon the docs site, and a door from the library to it. Your world renders live in a room, with the tools floating over it. An inspector generated from each component’s TypeScript edits every prop (sliders, palette-aware colors, enums, vectors, nested lists likeclothesoropenings).world.jsonedits two ways, with completion and checks. A liveApp.tsxruns in a sandboxed iframe and keeps itsworld.jsonimport in sync. A component lab has bounds, scale, wireframe, normals, colliders, stats, a seed grid, an A/B compare and a determinism check. There are templates, snippets, play mode, a ⌘K palette, undo, autosave, share links that carry the world in the URL, and exports (world.json, App.tsx, PNG, the prop schema). Gallery cards and component docs open their component in the lab. Personis configurable part by part, all as plain JSON:body(build,tone,height),face(eye color and size, brow color and weight,noseshape, lip color and fullness,blush),hairas{ style, color }, andclothes, an ordered list of garment layers (tshirt,shirt,sweater,vest,tunic,coat,dress,robe,apron,trousers,jeans,shorts,skirt,shoes,boots,belt,scarf,cape), each with its owncolorand, for tops,sleevesandneck. Layers go inner to outer and the order decides the fit: a shirt listed before the jeans is tucked in.clothes={[]}is the bare base body. The flat props (outfit,topColor,skinTone,hairColor, …) still work as shorthands. Newswepthairstyle andpreppyskin.useWorld().player: a ref to the mountedPlayer’s avatar (at eye height), ornullwhen there’s noPlayer. Components can react to where the player stands instead of where the camera is.- The ground is queryable:
groundAt(x, z)returns the highest walkable top at a point, computed from the same functions that build the meshes (no raycasts, no physics).createGroundIndex(world, registry)indexes a world once;useGround()gives the query inside a world;checkWorldreports problems. All of it also ships from the React-free@runek/core/dataentry, for Node scripts and tests. - Anchored nodes: a world node’s
anchor("ground"or"surface") makes itsposition[1]an offset above the ground at its (x, z), so props stay on uneven terrain. The file keeps the offset, so moving or reseeding the terrain re-seats everything anchored to it. Works for any node, including groups, composites, andPlayer. - Surfaces:
TerrainandShore(terrain),Dock,Floor, andSlab(decks), andLake(water) publish their tops asComponent.surface, with the math in a plainsurfaces/*.tsmodule thatrunek addcopies alongside the component. People, trees, furniture, and buildings (including thehouseandroomcomposites) are markedgroundSitting. - Editor: Drop to ground / Drop to surface for the selection, an Anchor switch that keeps the node in place, and anchored nodes slide along the ground when dragged.
runek check-world <file>: flags buried and floating nodes and open water standing above its shore, with a suggested Y for each.--fixwrites them into the file;--strictexits non-zero for CI.Terrainacceptsrotation, like every other component.- Guide: “Placing things on uneven ground”.
Personwalks.patroltakes waypoints relative to itsposition(they turn withrotation);wandertakes a radius and walks a seeded loop inside it.speed(by age: 1.3 m/s for an adult),pauseat each waypoint, andloop("loop"or"pingpong") shape the route. Where a figure is on its route depends only on the clock and the seed, so everyone viewing the world sees it in the same place. It follows the ground under it, eases in and out of each stop, turns into its corners, and swings its arms and legs in a walk cycle keyed to the distance walked, so feet don’t slide. The authoredposeplays at each pause (a merchant walks to the stall, then works). Walkers step aside for the player and for each other (passing on the right when head-on), and wait when a wall blocks both sides. A walking figure’s collider is kinematic; a standing one costs what it did.Persongait: the walk-cycle speed for a figure something else moves. As aPlayer’s body it now walks in step with the avatar instead of sliding.useWorld().walkers(the figures walking right now, for components that want to steer around them) andPlayerMotionContext(the enclosingPlayer’s live speed).Worldinput: setfalseto ignore the keyboard (e.g. while a modal is open). Keys held at that moment are released.Worldpaused: stops the frame loop and physics, so a world that isn’t on screen costs nothing. Animations resume where they stopped.isEditableTarget(target): whether a key event belongs to a text field rather than the world.Persontakes trips:routeis a one-way walk through waypoints (relative toposition, likepatrol) that sets off atdepartAt(epoch ms), waits at its first point before then, and stays at its last point after, in itspose. Where it is depends only on the clock, so every viewer agrees and a reopened page needs no replay.onArrivefires once per trip, even when the arrival happened while the world was paused or unmounted.tripAt()gives where a trip had got to, for starting the next one from there.Personactions,actionRadius, andonAction: walk near a figure and a prompt over its head shows what you can do (T Talk,I Info) with the keys the world’scontrolsbind; press one andonActiongets its id. Only the nearest figure in range shows a prompt and takes the key, and keys yield to text fields.Interactable: wrap anything in it to offer the player the same prompt and actions (sit on a sofa, use a desk).useInteraction(anchor, { actions, radius, onAction })in core, the hook behind both, for components that draw their own prompt;keyLabel(code)turnsKeyTintoTfor display.useWorld().keyboard: the key events the world reads (none aimed at text fields, none whileinputis off or the world is paused).Signoutline: a crisp edge in a color, so text reads against any background.Desk: a work desk on slim legs, a drawer pedestal, or side panels, with a modesty panel at the back. Base and drawer count are seeded.Monitor: a desk monitor on a foot, plate, or legs. Switched on, its screen glows in itsscreencolor with a few seeded windows of text on it;on={false}is dark glass.Sofa: one to four seats, with square, rolled, or slim arms, split or bench cushions in soft shades of the fabric, pegs or a plinth, and throw pillows, all seeded and each pinnable.- Guide: “Embedding a world in an app” (content security policy, desktop webviews, lazy loading, input, pausing, and figures driven by app state).
Personposestype(seated, fingers on the keys),play(hands on controls),drink(a mug held at the chest, sipped now and then), andlie(on its back, centered onposition, head toward -Z, so it takes aBed’s position and rotation at mattress height).Personbubble: a short line in a speech bubble over the head.emote: a small animated sign instead (sleeprising z’s,alerta bouncing !,thinkpulsing dots,happya heart,coffeea steaming cup), drawn from shapes and the world font.- Office and casual clothes:
hoodieandblazergarment layers,outfitshorthandstshirt,sweater,hoodie, andblazer, achefhat, and theoffice,casual,hoodie, andchefskins. Playerview="overhead"(andavatar: "overhead"in a world): the camera high above at a fixed tilt, following the avatar, with scroll to zoom and WASD walking relative to the screen. Aviewcontrol in the world’scontrolscycles first, third, and overhead at runtime, keeping the avatar where it stands.OfficeChair,Tv(on a stand, a console, or a wall, with a seeded picture),Whiteboard(seeded marker diagrams and optionaltext),Fridge,CoffeeMachine(espresso or drip, with steam),ArcadeCabinet(a seeded pixel game on its screen), andPoolTable(balls racked or mid-game).Tablebase="pedestal"andends="round", for a conference table;Counterhobfor a kitchen stove (a glass cooktop and an oven).SignonBounds: the laid-out text’s size, for fitting a backdrop.
Changed
- Breaking:
WorldEditormoved to its own entry point. Import it from@runek/core/editorinstead of@runek/core.levais now an optional peer dependency, needed only by apps that use the editor. - The world no longer reads keys typed into a text field (
input,textarea,select, orcontenteditable), so typing “wasd” into a form beside the canvas doesn’t walk the avatar. Keys held when the window loses focus are released instead of staying stuck down. @runek/clideclares Node 24 or newer (engines.nodewas>=20), matching the version the repo develops and tests on.Personhas a new look: one continuous sculpted body (no seams at the joints) with a sculpted face, eyelids that blink, eyes that follow you, and hands with fingers, skinned to an 18-bone skeleton so it bends as one. Clothes, hair, and hats are layers fitted to the body, so any garment fits any build, age, or style. The defaultstyleis nowstylized(an animated-film look);realisticandanimeremain. Meshes build in the background a few milliseconds at a time, coarse first; full detail builds only within ~10 units of the camera, and identical figures share one build.Personturns its head toward the player’s avatar, not the camera. In third person the camera trails behind, so figures used to look past you; now they meet your avatar’s eyes, andlookRadiusis measured from the avatar. Without aPlayer(e.g. an orbit view) it still watches the camera. APersonused as thePlayer’s own body no longer tries to track anything.Player’s capsule no longer tips over: its rotation is locked and only the visible body turns to face the way it walks. At low frame rates the old balancing could topple the avatar. The avatar also starts facing the way the camera looks.- A seated
Person’s name, bubble, and prompt sit over its head at seat height instead of floating at standing height. Playerholds still until there’s ground under it (or half a second passes) before gravity takes over, so a spawn can’t drop through a collider that hasn’t arrived yet.
Fixed
- The library’s gallery fits every exhibit to its pedestal: each
miniature is measured once, then scaled and centered, so large
pieces (
CurvedWall,Cliff,Clouds,Pool,Roof,Palm,Flag, …) no longer overhang the corridor or stand in front of their pedestal,Oceanshows again, and the emptyInteractableexhibit is gone. CurvedWallpreviews (gallery cards and its docs page) show a smaller arc centered on the frame instead of a wall standing in front of the camera.- Setting one
Persontrait (a skin tone, a build) no longer reshuffles the seeded rolls of the others. - A
Playerspawned over displacedTerraincould fall through it: the trimesh collider was derived one render after the mesh.Terrainnow builds it in the same render.
0.13.0 - 2026-09-18
Added
-
Personcomponent: a procedural human figure, so worlds can have people in them. The body is built from smooth lathe surfaces (tapered limbs, one hip-to-shoulder torso profile) on an articulated joint rig that breathes, shifts its weight, blinks, and turns its head to watch you; detail drops to a cheap silhouette past ~18 units. Astyletemplate sets the whole look:anime(default; bigger head, large lit eyes, tapered chin, longer legs, chunky fringe) orrealistic(canonical 7.5-head proportions). Traits resolve explicit prop first, then theskincosmetic bundle, then thekindrole preset (villager, merchant, guard, sailor, farmer, noble, scholar, traveler), then the seed, so<Person seed={3} />is a complete villager and every prop narrows it:gender,age,height,build,skinTone,hair,facialHair,outfit, clothing colors,hat,accessories, andpose.skin(aPersonSkin) bundles clothes, colors, headwear, hair, and accessories as one plain-JSON prop, so an editor or in-world UI can swap a figure’s whole look in a single write; nine named presets ship asPERSON_SKINS(festival, harvest, winter, voyage, ceremony, atelier, pirate, bartender, sailor — wardrobe only, never hair), andskinaccepts a preset name ("winter") or a bundle.positionis the spawn point; one capsule collider makes the figure solid, andphysics={false}yields a bare visual you can hand toPlayeras a third-person body. Movement and dialogue come later. -
Slabcomponent: a rounded structural plate (pillordisc) for curved-modernist floor decks, cantilevered roofs, terraces, or a lawn disc. Walkable, one convex-hull collider; top surface at the origin likeFloor. -
CurvedWallcomponent: an arc of wall around the origin —solidconcrete or a floor-to-ceilingglasscurtain wall with mullions and rails. Instanced chords, each carrying a cuboid collider; compose several arcs to leave door gaps. -
Poolcomponent: a built swimming pool flush with the deck — coping rim, walkable plastered basin with corner exit steps, translucent water just under the lip. -
Palmcomponent: a coconut palm with a leaning bowed trunk and a crown of drooping instanced fronds; seed drives lean, spread, and the coconut cluster. -
Docs library: a librarian and a traveler now stand in the reading room, and the player’s third-person body is a
Person.
Fixed
- Contribute modal: “Suggest changes upstream” downloads
world.jsonandsnapshot.pngwith one button instead of two, so the snapshot that step 4 asks for is no longer skipped. The PNG is now namedsnapshot.pngto match the instructions (wasworld-snapshot.png).
0.12.0 - 2026-07-19
Added
- Docs library: the reading-room shelves are labeled category sections
(Intro / Guides / Reference) instead of an unlabeled even deal, so a
new doc only joins its own section rather than reshuffling every
shelf. Reference docs (CLI, changelog) now shelve too, and the
changelog book reads the root
CHANGELOG.mdin-world. Books spread across each case’s rows (the CLI reference sits on the Reference shelf’s top row, the changelog on its bottom). The painted RUNEK mark carries the current version beneath it. BookSpec.shelf: pin aBookshelfbook to a specific row (0 = top, clamped); books without a row keep auto-packing bottom-up around the placed ones.Bookcomponent: a single procedural book —standing,lying, oropen— with a seeded cloth cover and optionalhref/onSelectinteraction (hover pop + title label). The standalone, placeable sibling ofBookshelf’s instanced spines; decorative, no collider. The gallery wing’s guide books now use it (open, on their stands).controls: input bindings are a world setting. A partialaction → KeyboardEvent.code[]map merged over the defaults, onWorldData/<World>and resolved atuseWorld().controls— remap for AZERTY ({ "forward": ["KeyW", "KeyZ"] }), disable an action with[], or add custom action names your own components read viauseKeyboardControls. Orthogonal toavatar; the JSXkeyboardMapprop remains the verbatim escape hatch and wins when given.- Composites: a new registry kind holding a named arrangement of
component nodes as JSON instead of code. A world places one node
(
{ "type": "House" }); the renderer expands the arrangement in place, and an instanceseeddeterministically re-rolls children that don’t pin their own. The editor lists composites in its add menu and gains an Unpack action that replaces an instance with an editableGroupsubtree.runek add <composite>copies the arrangement JSON plus the source of every part it references. (CONTRACT §11.) Levelcomponent: a stackable wall ring plus optional slab, with per-side wall configs (openings, color, or an omitted side) and a stairwell hole — the unit buildings are composed from.Floorgains a rectangularopening(a stairwell hole); the slab splits into strips around it so collision matches the visual.- Built-in
Groupnode type in worlds-as-data: a plain transform container for children. - Gable
Roofs cap their open end triangles (ends, default on) with wall-colored prisms, so the attic no longer shows the sky (Roof migration guide for keeping the old open look). scripts/migrate-buildings.mjs: bakes pre-compositeHouse/Roomnodes (with non-default props) into equivalent part arrangements.- Docs: “The building kit” guide; the library world’s gallery wing ends
at a walkable two-level show home built from the
housecomposite. - Docs: component pages gain a Migrate section — a side-by-side
before/after panel per version bump. Four components carry a
0.11.0 → 0.12.0 guide:
Wall,House,Room, andRoof. Entries live inapps/docs/scripts/migrations.mjsand accumulate across versions.
Changed
- Arrow-key camera look now works in first person too (it was third-person only): Left/Right turn, Up/Down look, in both views, composing with mouse-drag as before.
Bookshelfdefaults to 3 rows (shelveswas 4); passshelves={1}orshelves={2}for a shorter case, or any other count.- Breaking:
HouseandRoomare no longer coded components — the registry names now resolve to composites (the defaulthouseis a two-level dwelling with a working staircase). Worlds that placed them with default props keep rendering; worlds that relied on non-default props can bake the old look withscripts/migrate-buildings.mjs.HouseProps/RoomPropsare gone from@runek/components. Migration guides: House, Room. - Breaking:
Wall’s singleopeningprop is replaced byopenings: WallOpening[]— one wall can now carry a door and windows together. Migration (opening: X→openings: [X]): Wall migration guide.
0.11.0 - 2026-07-10
Added
Hut,Tent,Counter,Stool,Road, andCliffcomponents.CompassHUD component: a screen-fixed dial that tracks the camera heading, with an optional wind and bearing readout.Bookshelfgains alabelprop (pluslabelColorandlabelSize) that draws aSignfloating above the shelf frame.Pathgains aheightselevation profile so trails can follow the terrain they climb.Playergains achildrenslot for a custom avatar that replaces the default capsule and hides in first-person view.- Keyboard camera control: Left/Right arrows turn (yaw) and Up/Down arrows look (pitch), composing with mouse drag.
Changed
- Breaking: the default
keyboardMapin@runek/coreno longer binds the arrow keys to movement. WASD moves the character; the arrow keys steer the camera. - Docs site: search moved to Pagefind’s Component UI, and the walkable library world gained a gallery wing.
Fixed
- The brand font bundled in
@runek/coreis embedded as woff (was woff2, which troika-three-text cannot parse), so text components render the brand face instead of falling back to a fetched default. - Docs generator: props tables were missing for any component whose
props interface uses an
extendsclause.
0.10.4 - 2026-06-30
Added
Portalcomponent: a glowing travel gate with a physics sensor that firesonEnter(or navigates toto) when the avatar or a vehicle passes through.Signpostcomponent: a post-and-plank signboard that renders a name in the world’s display font.
Fixed
WorldNodesno longer passes an explicitnullchild to leaf nodes, which clobbered a component’s ownchildrendefault; text authored in world JSON (aSign’s label, for example) now round-trips correctly.
0.10.2 - 2026-06-29
Added
Dock,Flag,Sailboat, andWindmillcomponents.Sailboatgains aphysicsprop: set it tofalseto render the boat as a bare visual so a parent controller (a steerable vehicle) can own the physics body.- Prerelease publish channels:
just publish alpha|beta|rcships to the matching npm dist-tag and marks the GitHub release a pre-release.
0.10.1 - 2026-06-27
Added
Oceancomponent: a procedural animated-shader sea that follows the camera and blends into the distance fog, defaulting to the palette’s water colors and the world ground baseline.Terraingainsfalloff(radial island falloff with a shoreline at the rim) andcollider(setfalsefor backdrop terrain the player never walks).
Fixed
WorldEditorandWorldRendererforwardgroundandfontsto the world context instead of silently dropping them.
0.10.0 - 2026-06-26
Folds in the 0.7.0 through 0.9.0 milestones, which were versioned in the repository but never published to npm.
Added
- World identity and git-native contribution:
WorldData.meta(title, description, authors, license, source), stable nodeids withassignNodeIds(), a canonical key order inserializeWorldso unchanged nodes never churn a diff, and aWorldAboutpanel. - World settings and rules:
<World time>pins a reproducible time-of-day and<World timezone>tracks a live clock, driving a day/night cycle inSkyandLightRig;<World avatar>sets the default camera view forPlayer. - World fonts and the
Signtext component: a world declares fonts by role andSignrenders from them, with a default face bundled in@runek/core. Text is the single sanctioned exception to the asset-free rule. - 18 components rounding out the catalog:
Fence,Bridge,Path,Arch,Pillar,Bush,Flowers,Hedge,Well,Fountain,Bench,Bed,Crate,Barrel,Plant,Clouds,Campfire, andBirds. groundworld baseline (a Y value, default 0) thatFloorandLakeplace themselves against, plus the open-water rule inCONTRACT.md.- Docs site search (Pagefind).
Changed
- All workspace packages aligned to a single 0.10.0 version line.
- The release flow regenerates the served registry manifests before publishing and refuses to ship from a dirty tree, so manifests can no longer drift from the released version.
0.6.0 - 2026-06-18
Added
- Clickable
Bookshelfbooks: a serializablebooksarray ({ id, title?, color?, href? }) with hover feedback and anonBookSelectcallback; without a callback, a click navigates the book’shref. Clockcomponent: a procedural analog clock tracking the local system clock or a given IANAtimezone.
Changed
- Breaking: components import the runtime from the published
@runek/corenpm package instead of a copy vendored by the CLI, andcoreImportis removed fromrunek.config.json. Core improvements now reach existing projects via a normal dependency update. - Breaking: the CLI is published as the scoped
@runek/cli(previously the unscopedrunek). - Breaking:
Bookshelf’sfillnow defaults to0, rendering an empty shelf; passfillexplicitly for the old decorative look. - The editor exports the world as a
world.jsonfile download instead of copying JSON to the clipboard.
0.5.0 - 2026-06-18
Initial public release.
Added
@runek/core: the<World>provider (canvas, physics, keyboard controls, lighting),useWorld, deterministic seededrnghelpers, world palette and fog, and the shared component contract.- Data-driven worlds: the
WorldData/WorldNodeJSON model withserializeWorld/parseWorld, aWorldRendererthat mounts world data against a component registry, and an in-browserWorldEditorwith selection gizmos and JSON export. - The
runekCLI (init,add,list) and the source registry:addcopies editable component source into your project along with its dependencies. - Starter catalog:
Player,Terrain,Room, andBookshelf, plus the structure, furnishing, nature, and lighting component sets. - Docs site with flat pages, a live component gallery, and a walkable 3D library world.