An open world that comes alive as you build in it.
Aava /ˈɑːʋɑ/ — Finnish for vast, open expanse, said most often of the open sea (aava meri) or a wide, unobstructed landscape. Where the word itself comes from is not settled: it may be a variant of aapa, the term for the open, treeless mires of Finland's north, shifted by association with avata, "to open"; or a separate borrowing from Sámi. Either root points the same way — a country you can see all the way across, and build anything in, which is the freedom the game is actually built around. Finland is also, out of all proportion to its size, a games country — Supercell, Rovio, Remedy — and a small project made by one person on evenings and weekends is not a strange place to borrow a word from it.
Aava is a 3D exploration-and-building game for children, written in GDScript on Godot 4. The valley is a pure function of one integer seed: the ground, the river, the forest and the mountains are computed rather than stored, so nothing about the world has to be saved, sent between two machines playing together, or loaded from a file. What is saved is only what a child has changed — see Every world is one integer, below.
The loop is creation rather than collection. Everything built makes the valley more alive, and the more alive it is the more it gives back: plant a grove and birds arrive, feed an animal and it becomes a friend, raise a house with a bed and a fire and it becomes somewhere to actually spend the night rather than a shape standing in a field. Coins come from looking after things, and are spent on tools that widen what a child can reach — an axe, shears, a lantern, a bicycle, a motorcycle, a quad, a horse to ford the river a wheeled machine cannot — rather than on numbers going up.
Since the first line of this file was written, the valley has grown mountains with snow on their shoulders and ice in their hollows, a river long enough to be worth following, two lakes — one of them up a hillside, holding its own water at its own height — a day/night cycle with a lantern for the dark, a bow range, a herd of twenty horses and a shop to buy a mount from, a flock of sheep to shear and cows to milk, an economy where felling a tree costs what growing one paid, campfires that need feeding and beds worth sleeping in, animals that speak on their own terms, and a whole ambient soundscape synthesised from nothing but code.
There is a fairground across the river with a carousel, two moving walkways, a trampoline, a fifty-metre wheel, a drop tower, a pirate ship and a wooden coaster with two vertical loops, whose bars come down before anything moves, and whose clocks the host's phone shares with a guest's, whose speed is integrated from the slope of its own track — the loops are sized by what the drop actually pays out, v² ≥ 5gR, rather than by eye. A bridge crosses to it, because a machine on wheels cannot ford the river and a horse can; street signs stand where the four roads meet, each turned by asking where its own road actually goes. And a way to invite another player into your own copy of the valley over the local network, and talk to them while you play.
Requires Godot 4.7 or newer. No other dependencies.
godot --path . # play
godot --path . -- --seed=12345 # play a different valleyMove with the on-screen stick or WASD, look around by dragging anywhere else on the screen, sprint with shift, jump with space. Walk over a stick, a stone, a reed, a seed or a cone and you pick it up — there is no button for gathering, because walking into things is something a six-year-old understands without being told. Tap build to open the palette; the ghost shows what will be placed, green when it can be and red with the reason when it cannot. See docs/CONTROLS.md for the full list.
Walk south-west and you find a football pitch by the river. Stand next to a
ball and a kick button appears; press it, or E, and the ball goes. Hold
it and slide the thumb to turn or lift the shot; run at the ball and it goes
much harder — that is the whole skill of shooting, and it is worth more to a
six-year-old than any charge-up meter. Balls that end up in the
river come back on their own.
Plant three saplings close together, keep playing, and they grow. When all three
are full trees the place becomes a grove and birds arrive. That loop — build,
and the world answers — is what the whole game is for. The stick is Godot 4.7's native
VirtualJoystick, so both fingers work at once without any index bookkeeping:
the viewport keeps routing each finger to the control it first landed on.
Walk north-east and there is a shop: a green-boarded barn with a porch, with five bicycles and two motorcycles standing on its floor and somebody behind the counter. Its shelf is pictures and prices, because a shop of words excludes the child most likely to be saving up; tap a picture and it says what the thing is, tap buy and it is yours. The coins come from looking after animals, and from the wool you cut off the sheep — the one thing the shop buys rather than sells.
Build a house, feed its campfire, and sleep in its bed after dark to wake up
rested at dawn — the reason to have built the house at all rather than a shape
standing in a field. Invite a sibling into your own copy of the valley from the
≡ menu, and talk to them by holding talk: push-to-talk only, the
microphone physically stopped between presses, and reachable only by whoever
you actually invited.
Every world is one integer. There is no hand-placed scene content anywhere in this project: the valley, the mountains, the river, the lakes and everything scattered on them are generated from the seed, which is the only way one person can make a world large enough not to be exhausted in an afternoon. It also means any bug can be reproduced by sharing a number, and that two devices joined over the family network never have to send each other the ground — both already made the identical valley from the same seed.
This project is written from a terminal, so there is a tool whose whole job is to answer "what does it actually look like right now":
godot --path . res://dev/capture.tscn -- \
--out=/tmp/shot.png --pos=-2,4,30 --look=26,1.2,-6 --time=0.40| Flag | Meaning |
|---|---|
--out=PATH |
where to write the PNG |
--pos=x,y,z |
camera position |
--look=x,y,z |
what it points at |
--time=0..1 |
time of day, 0.5 is noon |
--seed=N |
which valley |
--nowater=1 |
hide the water sheet, to see the ground under it |
--unshaded=1 |
draw terrain as flat vertex colour, no lighting |
--player=1 |
spawn the real player and shoot through the game camera |
--yaw=DEG, --pitch=DEG |
aim that camera |
--demo=1 |
stand a finished camp and a grown grove in front of the player |
--build=1, --house=1 |
open build mode; add a finished house nearby |
--atpitch=1 |
stand the player on the football pitch |
--kick=SEC |
kick the nearest ball, then watch it fly for this long |
--mounts=1, --animals=1 |
put a horse and a bicycle, or one of every animal, in view |
--dam=1 |
finish the first beaver dam, to show the pond behind it |
--shop=1, --together=choice|host|type |
open the shop, or the play-together panel, on a chosen page |
--lang=en|fr|ru |
which language to draw the interface in |
That is a representative subset — src/dev/capture.gd's own header comment is
the exhaustive, current list. The last two rows above exist because a
screenshot cannot tell you whether the colours are wrong or the light is
wrong, and guessing between the two is expensive. The tool also prints how
many chunks and triangles were actually built and where the camera ended up —
which is how the inverted triangle winding that made the whole valley
invisible from above was finally caught.
The checks CI runs, which are worth running by hand before a commit:
godot --headless --fixed-fps 60 --path . --script src/dev/ci_check.gdEvery assertion in there corresponds to a bug that actually happened. The one
that earns its keep most often is the pickup count: it caught a world with
nothing to find, because resources were being built in _ready, which never
runs for a node used before the scene tree starts. See
docs/LESSONS.md for the whole catalogue — it is long, and
every entry in it is a bug that cost real time and is now guarded against.
The Android preset is committed, so a build is one command. It uses Godot's prebuilt template rather than a Gradle build, which means no Gradle and no Android project directory — just the SDK and a JDK.
godot --headless --path . --export-debug "Android" build/aava.apk
adb install -r build/aava.apkWhat has to be set once, in Godot's editor settings:
| Setting | Value |
|---|---|
export/android/android_sdk_path |
~/Library/Android/sdk |
export/android/java_sdk_path |
a real JDK 17+ |
export/android/debug_keystore |
a debug keystore |
export/android/debug_keystore_user |
androiddebugkey |
export/android/debug_keystore_pass |
android |
Two things that cost time if you do not know them. /usr/bin/java on macOS is a
stub that only tells you to install Java, so java -version succeeding proves
nothing — point java_sdk_path at a real JDK, such as the one Android Studio
bundles at Contents/jbr/Contents/Home. And
rendering/textures/vram_compression/import_etc2_astc must be on or the export
aborts, because Android requires ETC2/ASTC textures.
This is the actual target device, not an afterthought: the game has been installed and played on a real tablet repeatedly, which is how bugs invisible in a desktop screenshot — the game locked to portrait, a player launched into the sky while swimming, a horse vanishing the instant it was mounted — were actually found. See docs/ANDROID.md for the toolchain and what the performance numbers actually say.
iPad needs a paid Apple developer account and is not set up yet.
Anchor ignore patterns. A plain build/ line in .gitignore matches at
every level, including src/build/. The whole building system was silently kept
out of the commit that added it: the game ran perfectly here and CI failed with
Could not find type "Structures", because CI was the first machine to see the
tree as it had actually been pushed. Ignore patterns for build output are now
written /build/.
Import twice on a clean checkout. Global class_name types only exist once
.godot/global_script_class_cache.cfg has been written, and on a fresh clone
that file is written by the same pass that parses the scripts. Your own machine
never shows this, because the cache is left over from last time.
godot --headless --path . --import # writes the class cache
godot --headless --path . --import # this one should be silentTo see the project as a fresh clone sees it, delete .godot/ first — and check
git ls-files src against what is actually on disk.
src/
main.gd entry point: reads the seed, builds the world and the game
world/
height_field.gd the shape of the world as a pure function of (x, z)
terrain_spec.gd shared terrain constants, dependency-free on purpose
terrain.gd chunk streaming and detail rings
terrain_chunk.gd one square of ground: mesh, vertex colours, collision
water.gd the water sheet and its shader
atmosphere.gd sky, sun, a full day/night cycle
plant_meshes.gd trees and grass, built from primitives in code
vegetation.gd planting, streamed in tiles around the player
vegetation_tile.gd one tile of planting: a MultiMesh per plant kind
world.gd assembles the above; the one place a world comes from
pickups.gd sticks, stones, reeds, seeds and cones, gathered by walking,
and laid again out of sight after a while
birds.gd the world answering: birds over feeders and groves
pitch.gd where the football pitch is, and how it levels the ground
lakes.gd two lakes, each holding its own water at its own height
distant_land.gd the far country: one coarse ring out to the mountains
paths.gd worn routes between the places worth going
place_spec.gd where the playground, pool, café, shop and butts stand
places.gd the buildings and what they offer: the swing, the slide,
the café's meal, the pool's turnstile, the shop's counter
dam_spec.gd, dams.gd a beaver dam site, and what it does to the river
animal_kinds.gd, animals.gd six species: habitat, shyness, and what each
one gives — coins, or wool from a sheep and milk from a cow
animal_collision.gd, tree_collision.gd what is solid near you, in a small
pool of bodies that follows the player about
ducks.gd ducks on the ponds, which give nothing and are the point
mount_kinds.gd, mounts.gd the horse, the bicycle, the motorcycle, the boats
hearths.gd campfires: fuel, warmth, the flame itself
park.gd, park_spec.gd the fairground and where it stands; its rides:
carousel.gd, ferris_wheel.gd, roller_coaster.gd,
drop_tower.gd, pirate_ship.gd, walkway.gd
fair_decor.gd, ride_sounds.gd its lamps, stalls and signs; its hum
bridge.gd, bridge_spec.gd, signpost.gd, plaque.gd the crossing and the street signs
clear_ground.gd ground that is built over, where grass must not grow
respawn_spot.gd where a child is saved and may reappear
animal_loft.gd, animal_natural.gd, animal_small.gd, horse_natural.gd
the animals as smooth painted skins
wind.gd, flag.gd, sail.gd one wind, and what flies in it
boulders.gd the jumpable rocks
felled.gd stumps left behind by the axe
football/
ball.gd a football, with physics rather than an animation
goal.gd posts you can hit and netting that kills the ball
football_ground.gd goals, balls, scoring, and fetching strays
kick_preview.gd where the ball will go, drawn while you wind up
pitch_markings.gd, scoreboard.gd the lines and corner flags, and the board
archery/
archery.gd the range, the butts, and the bow itself
player/
player.gd the controller: acceleration, coyote time, jump buffer
camera_rig.gd third-person camera that glides instead of ticking
character.gd, rig_motion.gd, seat_pose.gd five characters, how they move,
and how they sit in every seat
lantern.gd the light a child carries after dark
build/
build_kinds.gd what can be built, what it costs, what it grows into
house_parts.gd walls, doors, windows, floors, roofs, stairs, a bed
structures.gd what has been built, and what it has since become
plans.gd, venues.gd, keep_out.gd ready-made buildings, what a built café
or shop does, and ground that belongs to something else
build_mode.gd the ghost preview, snapping, and the one overlap rule
shop/
wallet.gd coins, and what has been bought
shop_stock.gd what is for sale, and what it costs
game/
item_kinds.gd what can be picked up
inventory.gd what is being carried
vitals.gd energy and water
journal.gd what a child has done, counted rather than owned
today.gd, tasks.gd one thing worth doing each day, and the opening steps
profiles.gd players, map templates, and the worlds made from them
save_game.gd the save file, as readable JSON, split by player and world
dishes.gd the café's six dishes
wiring.gd how the interface and the game are connected, once
net/
session.gd LAN multiplayer: hosting, joining, what gets sent
visitors.gd drawing the other children in the valley
ride_sync.gd the host's rides, followed by the guest's phone
voice.gd push-to-talk voice chat
audio/
sounds.gd short sound effects, synthesised at load
ambience.gd wind, water, birds and leaf rustle, positional and looping
animal_voices.gd barks, chatter, grunts and purrs, as single events
mix.gd the buses: a limiter on Master, World, Effects
ui/
camera_pad.gd the look-around touch layer
hud.gd every on-screen control
minimap.gd, map_arrow.gd, place_glyph.gd the map, the arrow that shows
which way you face, and the picture for each destination
map_view.gd, map_gesture.gd, map_painter.gd where it looks, how fingers
move it, and how it is painted
map_guide.gd, guide_badge.gd, map_route.gd, map_legend.gd, map_tool.gd,
map_compass.gd the chosen place and its pointer, the route, the key,
the round buttons and the peak that says north
hold_ring.gd, volume_look.gd a press that must be held; the volume sliders' look
hud_fly.gd, note_icon.gd a coin or a chip flying to the purse or the bag;
the picture beside an announcement, for a child who cannot read
look_picker.gd, character_icon.gd, flag_icon.gd, dish_icon.gd, coin_icon.gd
choosing who you are and the other drawn pictures
action_icon.gd, item_chip.gd every button's picture, and every item's,
drawn in code rather than loaded as art
backpack.gd, vitals_gauge.gd, part_icon.gd, shop_icon.gd the rest of the interface
together_panel.gd hosting, joining, and the join-code keypad
i18n/
text.gd every user-visible string, in English, French and Russian
input_actions.gd input actions, registered in code
dev/
capture.gd screenshot tool
toddler_fuzz.gd, net_pair.gd a random-tapping child; two processes as a pair
net_day.gd, net_rogue.gd a scripted twelve-minute day of two phones, and a
misbehaving peer that the limits must hold off
ci_check.gd the numeric check suite CI runs on every push
perf_log.gd which step of the frame was slowest, named in the log
height_field.gd is the single source of truth for where the ground is.
The mesh, the collision heightmap, and every tree, animal and building ask
it — there is deliberately no second opinion about the ground anywhere, which is
what keeps trees from floating and the player from falling through hills. See
docs/ARCHITECTURE.md for the rest of how this is put
together, and docs/STATUS.md for what is actually finished
today.
See docs/DESIGN.md.
A CITATION.cff sits in the repository root, so GitHub's "Cite this repository" button renders BibTeX and APA directly. The same entry, to copy:
@software{Organokov_Aava_2026,
author = {Organokov, Mukharbek},
title = {{Aava: an open world that comes alive as you build in it}},
year = {2026},
version = {0.8.0},
doi = {10.5281/zenodo.22965441},
url = {https://github.com/kabartay/aava},
publisher = {Zenodo},
}There is no paper. This is a game made for two brothers, and what there is to cite is the thing itself — the software, and whichever build of it you played.
Every release is archived on Zenodo with a DOI of its own. The concept DOI 10.5281/zenodo.22965441 always resolves to the newest release; a version DOI — v0.4.1, for instance — stays on the build it was minted for. Cite a version when it matters which one somebody actually played, because a valley three releases old is a different valley: it had no fairground, no bridge, and horses that faced the wrong way while grazing.
Citing the software is not permission to use the name or the icon — those are reserved, and the Licence section below says what that means.
Code: AGPL-3.0. See LICENSE. Read it, change it, build it, play it; a changed version you pass on has to stay open too.
The name and the look are reserved. "Aava", the icon and the visual identity that goes with them are not covered by that licence — see NOTICE.md. Fork it under your own name and your own icon and you are welcome.