Skip to content

Saving & Opening Projects ​

An Awaken project is your working file: the complete, editable state of everything in the editor, saved as one binary .awaken file. You save it while you work; the game exports are read-only files built from it.

Where a game.html carries only the assets a game uses (compacted and quantized for size), a .awaken project carries everything: every imported mesh, texture, sound, font and UI image, the whole scene library, prefabs, materials, shaders and shader graphs, scripts, the skeletal-animation library (skeletons, clips, rig-space clips, skins), particle effects, UI animation clips, render textures, Simulations, compute shaders, render settings, App Settings, Project Settings (Time, Quality, render layer names) and the import-source paths. You cannot open a game.html in the editor; keep the .awaken.

The .awaken format ​

A .awaken file is a binary container with a glTF-.glb-style layout, not JSON:

  • Bytes 0 to 3 are the magic AWKN, bytes 4 to 7 the PROJECT_VERSION and bytes 8 to 11 the length of the JSON that follows (all little-endian u32, packages/assets/src/persist.ts).
  • The JSON metadata holds the scene graph and every structured record. It is zero-padded so the blob starts 4-byte aligned.
  • One binary blob holds every byte span, each 4-byte aligned, named from the metadata by [offset, length], with no base64 and no number-array JSON. On Crown Quest's project the baked clips were 42.4 MB as JSON and are 7.6 MB as spans plus 0.3 MB of JSON.

Versions ​

The current format is PROJECT_VERSION = 15.

VersionWhat it added
15Each RGBA texture's span is a PNG file (png: true): Paeth on every row at zlib level 2, or no filter at level 4 when a sample of the rows (8 in every 128) comes out smaller. A device-encoded texture keeps its DEFLATEd bytes (z). Schwizzle's project, mostly one 3072 x 2048 photo-like atlas, went from 18.5 MB to 13.0 MB, lossless.
14The open scene is saved once: sceneRef names the library scene it is a copy of, instead of a second copy under scene. Baked clip keys are exact float64 blob spans instead of JSON. Crown Quest's project went from 59.4 MB to 22.7 MB with nothing lost.
13Each font face has a family, weight, style, variable flag and licence.
12Simulations and the Time settings (Project Settings).
11Render textures and render layer names.
10Compute shader assets.
9App Settings, with their images in the blob.
8UI images, kept as their own encoded files.
7Texture pixels DEFLATEd in the blob.
4 to 6Scripts: v4 native module assets, v5 a language per script (typescript, assemblyscript or c, folding v4's modules map into scripts), v6 each script's uses list.
3The skeletal-animation library (skeletons, baked clips, rig-space clips, skins) and particle effects.
2Meshes quantized: positions Uint16 over the mesh AABB, normals octahedral Int8, UVs Uint16 over the UV bounds, about 32 to 12 bytes per vertex.
1Raw Float32 meshes.

Every older version still opens; the next save writes it as v15. A build refuses a project newer than itself ("this project is version 16, but this build reads up to 15") rather than open it and drop what it cannot read on the next save. Mesh quantization is lossy but idempotent: a value already on the grid re-quantizes to the same index, so re-saving never adds error. Indices and vertex colours are kept exact, and decodeProject dequantizes to Float32 on load. Full field list on Data Formats.

NOTE

The workspace dock layout is not restored from the project. It is a per-machine preference kept in localStorage, so opening a colleague's project never changes your panel arrangement.

Saving in place (Chromium) vs download/upload ​

Awaken uses the browser's File System Access API to save a project in place to a real file on disk, like a native app's Save, with no browser storage quota and no eviction. The asset bytes live in that file, never in browser storage.

This API is Chromium-only. On Firefox and Safari, Awaken falls back to download and upload:

Chromium (File System Access)Firefox / Safari (fallback)
SaveWrites back to the same file, no dialogDownloads a new project.awaken each time
Save AsNative save-file pickerSame download
OpenNative open-file pickerHidden <input type="file"> upload
Open Recent and reopen on startYes (handles remembered)No

Recent projects and reopening ​

When you save or open on Chromium, Awaken stores the FileSystemFileHandle in IndexedDB (the last one under the key projectHandle, plus a recent list). Only the handles are stored there; the asset bytes stay in the file on disk.

  • File ▾ → Open Recent lists the recent projects, newest first, with Clear recent list at the bottom. Clicking one asks the browser for permission (one click) and opens it.
  • The Welcome page lists the same recent projects, the last one marked. While the Welcome page opens at start, the editor asks no "Reopen last project?" question.
  • With the Welcome page switched off, the editor asks "Reopen last project '‹name›'?" as it starts. Declining stops the question for that project until you open it again yourself (Open Recent or Open Project…). See Launching the Editor.

Saving, and the ⌘S habit ​

Save with ⌘S (Ctrl+S on Windows/Linux), or File ▾ → Save Project. ⇧⌘S / File ▾ → Save Project As… picks a new file (suggested name project.awaken). The shortcut works from anywhere in the editor, a text field included, and replaces the browser's own Save dialog.

The File ▾ menu: Open Project…, Open Recent, Save Project and Save Project As… with the scene and export items.

The project name in the Toolbar shows a • dot while you have unsaved edits, and its tooltip reads "- unsaved" / "- saved". A project never saved shows Untitled.

Auto-save is off: save explicitly ​

IMPORTANT

Awaken does not auto-save. Saving rewrites the entire file (every mesh and texture, potentially hundreds of MB) atomically. Doing that every 1.5 s of idle thrashed the disk and Chrome's swap temp files, so it was removed. Save deliberately with ⌘S.

Two guards protect your work:

  • Unsaved-changes warning. A beforeunload guard raises the browser's "Leave site?" prompt if you close or reload the tab while the project has unsaved changes (not in a browser driven by automation).
  • No save during Play. Save is blocked while the game is playing and logs "Stop play before saving": saving mid-Play would bake transient runtime state (physics positions, accumulated rotations) into the saved scene. Stop first; the pre-play scene is restored on Stop.

Opening a project ​

File ▾ → Open Project… opens the file picker (Chromium) or the fallback upload input. The file is read and decoded behind a progress dialog with these phases: Reading file → Decompressing → Loading N meshes → Building terrains (when there are any) → Loading N textures → Compiling scripts i/N → Building scene. Scripts are recompiled from their source on open; one that fails to compile is reported to the Console without stopping the rest of the load. The Console also logs the time each phase took.

Opening a project replaces the current editor state (scene, scene library, prefabs, materials, scripts); it is not a merge. Once it is open, the editor brings the Scene view forward if the Welcome page was the visible tab. A project that cannot be opened shows a Couldn't open the project notice with the reason, as well as the Console line.

A project as plain files (for git) ​

A .awaken is one binary file, so git sees every save as one changed blob. Three terminal commands, run in the engine's folder (awaken/editor, they need Deno), turn it into a folder of readable files and back:

sh
pnpm awaken:unpack game.awaken [game/]      # write the folder (default: game/ beside the file)
pnpm awaken:pack game/ [game.awaken]        # write the .awaken (default: game.awaken beside the folder)
pnpm awaken:check game.awaken [game/]       # exit 1 when the two disagree, saying which changed last

Relative paths are taken from the folder you ran pnpm in (INIT_CWD). The code is scripts/awaken-project.ts over packages/assets/src/projectFolder.ts.

The folder holds the same JSON cut into pieces, and the same bytes, one file per blob span:

PathWhat it holds
project.jsonThe metadata's top level in its own order: settings, App Settings, render and the other small records inline; each part kept elsewhere is named by its file ("scenes": "scenes/index.json").
scene.jsonThe open scene, only when it is not a copy of a library scene (otherwise sceneRef names that one).
scenes/, prefabs/index.json (name to file) and one pretty-printed <Name>.json per scene or prefab, entities in the engine's order, so a one-field edit is a one-line diff.
scripts/index.json (ids, names, uses) and each script's source as <Name>.ts, .as.ts or .c.
shaders/, shaderGraphs/, subgraphs/, computeShaders/One JSON per material, its WGSL hooks as .wgsl; compute shaders as <name>.wgsl.
textures/Each texture as <id>.png, the PNG file the project holds, and index.json for the rest.
sounds/, ui/fonts/, ui/images/, app/The encoded files as they were imported (.opus, .m4a, .otf, .png ...).
meshes/, animation/, terrains/, plugins/, render/Numeric spans as <id>[.<field>].bin, with their headers (bounds, counts, channel lists) in JSON.
particles/, simulations/, ui/clips/, materials/, renderTextures.json ...One small JSON per record or per kind.

How the round trip behaves:

  • Byte for byte. pack(unpack(f)) is f byte for byte for a file saved by the current codec. Offsets are never written down: the blob is laid out again in the order the codec writes it, so one asset changing size touches no other file. Unpacking checks itself and throws rather than write a folder that would not pack back to the same bytes.
  • Older files. A file from an older version (or not laid out as this codec writes it) is read and saved by the codec first, and the folder holds that. unpack prints a note saying why.
  • Unpack makes the folder exactly the project. Files the project no longer holds are deleted, unchanged files are left alone (their times too), and emptied folders are removed. It refuses to write into a folder that is not empty and has no project.json, so a mistyped path never empties a directory.
  • Pack writes the .awaken only when its bytes change. A .png in textures/ is decoded on the way in, so a texture you replaced in the folder (any 8-bit PNG the decoder reads) packs at its new size, and one the engine cannot open is refused there and not when the project is opened.
  • Check compares the folder, packed, with the file. When they differ it says which was changed last, by modification time: "was saved after the folder changed: unpack it" or "the folder changed after ... was written: pack it".

Keep the folder free of end-of-line conversion in git (-text in .gitattributes): a script is its exact text, and a converted line ending packs to different bytes.

See also ​

Awaken: browser-native WebGPU game engine.