This logo is rendered in-engine by Diorama: pixel-art sprites composited in world space with the gem's own CRT scanline pass.
World-space 2D and 2.5D for the Open 3D Engine.
Diorama is an O3DE gem that renders sprites and tilemaps as world objects through Atom, so flat content lives inside real 3D scenes with lighting, physics, and depth. It is the world-space counterpart to LyShine's screen-space UI: LyShine owns the UI layer, Diorama owns world-space 2D content. It ships as a clean, upstreamable gem, not an engine fork.
Diorama is in beta. The feature set is broad (see the status table below) and the gem builds with its unit tests green on both Linux and Windows. The API, serialized formats, and component layout may still change before 1.0 without a compatibility guarantee, so it is not yet recommended for production. Feedback and issues are welcome.
O3DE has no first-class path for world-space 2D. LyShine is screen-space UI and Atom is a 3D PBR renderer, so building a 2D or 2.5D game today means working against the engine: abusing UI canvases, hand-rolling quads, or bolting sprites onto 3D meshes. Diorama fills that gap by rendering 2D through Atom, which also gives 2.5D for free: camera-facing billboards, depth-sorted layers, and sprites freely mixed with 3D geometry, lighting, particles, and post effects. See VISION.md for the full rationale and design priorities.
- 2D: flat content composed in the world. Sprites, tilemaps, sprite-sheet and skeletal animation (cutout, DragonBones weighted-mesh deform, and surface / FFD deform), orthographic and pixel-perfect cameras.
- 2.5D: those flat elements living inside a 3D scene, with depth sorting, parallax, and free mixing with 3D content. This is the sweet spot pure-2D engines cannot reach.
| Area | Capability | Status |
|---|---|---|
| Sprite | World-space sprite quad rendered through Atom | Working |
| Sprite | Editor viewport preview via a shared presenter | Working |
| Sprite | Atlas UV sub-regions, horizontal/vertical flip, anti-diagonal transpose (90° rotations) | Working |
| Sprite | Billboard (camera-facing) and fixed orientation | Working |
| Sprite | Point filter (crisp pixel art) | Working |
| Sprite | Sprite-sheet / flipbook animation + frame events (OnAnimationFrame) |
Working |
| Sprite | Playback speed / time-scale (hit-stop, slow motion) | Working |
| Sprite | Afterimage trails (fading ghost copies; dash / super effect) | Working |
| Sprite | Palette recolor (three-stop luminance ramp; team / alt colors) | Working |
| Rendering | Batched feature processor (texture + sort-layer batching) | Working |
| Rendering | Automatic camera-distance depth sort | Working |
| Rendering | Off-screen sprite culling (view side-plane frustum reject) | Working |
| Rendering | Soft ground shadows under billboards | Working |
| Tilemap | Atlas-grid tilemap component + typed bus | Working |
| 2.5D | Depth-sorted layers + tilted 2.5D camera | Working |
| Scripting | Typed per-feature request buses (Lua, Python, ScriptCanvas) | Working |
| Gameplay | 2D collision: colliders, triggers, and queries reachable from scripts | Working |
| Gameplay | Pushbox resolution (ComputeBoxPushOut minimum-translation) |
Working |
| Gameplay | Frame-data hitboxes/hurtboxes (OnHit/OnHurt over the 2D collision world) |
Working |
| Gameplay | Typed interaction boxes: pushbox/throwbox/armor/proximity + attack payloads on OnBoxEvent, clash/armor/throw matrix, world-space box overlay, bone-attached boxes |
Working |
| Gameplay | One-way platforms + ramp ground-follow (ProbeGroundY, side-scroller) |
Working |
| Input | Rebindable input action mapping (Button / Axis1D / Axis2D actions) | Working |
| Input | Motion inputs (numpad sequences: quarter-circle, dragon-punch) | Working |
| Gameplay | 2.5D brawler depth lanes (depth lift/sort + depth-aware combat) | Working |
| Gameplay | Grid intelligence cores: FOV/fog, movement range, A* pathfinding | Working |
| Simulation | Fixed-step 2D Simulation Clock (OnSimTick, pause/single-step) + seeded RNG |
Working |
| Simulation | Snapshot/restore with state hash + slots (rollback-ready; CI determinism proof) | Working |
| Simulation | Per-sim-frame input ring: frame queries + InjectActionState (replays, bots, rollback) |
Working |
| Simulation | Per-component sim-clock advance (Use Simulation Clock) + timed super-freeze (FreezeFor, cinematic pause) |
Working |
| 2.5D | Parallax background layers | Working |
| Camera | 2D camera controller (follow, deadzone, bounds, shake) | Working |
| Camera | Versus framing (two-target midpoint) + distance zoom/dolly | Working |
| Camera | Orthographic / pixel-perfect camera | Working |
| Lighting | 2D dynamic lights + normal-mapped sprites | Working |
| Lighting | Day/night cycle (time-of-day color/intensity/direction over a light) | Working |
| Effects | 2D particle emitter | Working |
| Effects | Bullet-pattern emitter (danmaku: ring/fan/spiral, pooled, collidable) | Working |
| Effects | Sprite materials (flash, outline, emissive/bloom) | Working |
| Tilemap | In-editor tile paint tool (editor component mode) | Working |
| Tilemap | Autotiling: 4-bit edge set and 47-tile blob (corner-aware) | Working |
| Tilemap | Custom rule-tile autotiling (neighbor-mask → tile offset) | Working |
| Post | 2D Look: bloom + vignette over Atom's PostProcess | Working |
| Post | Retro CRT scanline overlay | Working |
| Animation | Skeletal cutout clip player (keyframed bone hierarchy) + cross-fade + 1D blend trees | Working |
| Animation | Skinned Sprite mesh deform (DragonBones weighted-mesh import + CPU skinning + clip playback) | Working |
| Animation | Surface + FFD deform (DragonBones surface rigs: control-point-grid warp, nested surfaces, per-vertex FFD, type-40 parameter composition) | Working |
| Animation | Aseprite sprite-sheet import (tags + per-frame timing) | Working |
| Animation | Animation state machine (parameter-driven clip switching) | Working |
| Asset pipeline | Native .aseprite AssetBuilder (packs atlas + sheet metadata) |
Working |
| Asset pipeline | Native .aseprite: indexed + grayscale color depths, separable blend modes |
Working |
| Asset pipeline | Runtime asset reference: play a .dioramasheet product directly |
Working |
| Audio | One-shot SFX + music via MiniAudio | Working |
| Project | Diorama2DGame "New 2.5D Game" project template |
Working |
| Asset pipeline | Dedicated tilemap asset + builder (.dtilemap JSON or Tiled .tmj → validated .dtilemapc), multi-layer + per-tile flip/rotate |
Working |
Editor preview scope: components that have a viewport preview (Sprite, Tilemap, 2D Light, 2D Look, Skeletal, Skinned Sprite, Aseprite) live-update as you edit their Inspector properties. Behaviors that only exist at run time (input, collision, particles, camera follow, the animation state machine, CRT and parallax scroll) and any state set through the runtime request buses are seen in play / game mode, not in the static edit-mode preview. This mirrors O3DE's split between authored state (the Inspector and the saved prefab) and runtime state (transient, not written back to the prefab), and is by design rather than a pending fix.
- Open 3D Engine 26.05 (built and verified against the 26.05 SDK).
- The Atom_RPI gem (a Diorama dependency, included with O3DE).
- A C++ toolchain and CMake matching your O3DE setup. Linux and Windows are verified (the gem builds and its unit tests pass on both); macOS is targeted but unverified. On Windows, Visual Studio 2022 or 2026 with the C++ workload.
Register the gem with your engine, enable it in a project, then build the project. Replace the paths with your own.
# 1. Register the gem with O3DE (one time)
<engine>/scripts/o3de.sh register --gem-path /path/to/o3de-diorama
# 2. Enable it in your project
<engine>/scripts/o3de.sh enable-gem --gem-name Diorama --project-path /path/to/YourProject
# 3. Configure and build the project (profile config shown)
cmake -B /path/to/YourProject/build/linux -S /path/to/YourProject -G "Ninja Multi-Config"
cmake --build /path/to/YourProject/build/linux --config profileThe same register / enable / cmake steps build any configuration; swap
--config profile for debug (unoptimized, for stepping through gem code) or
release (the optimized shipping build).
On Windows, use o3de.bat and a Visual Studio CMake generator (Visual Studio 18 2026 for VS2026, Visual Studio 17 2022 for VS2022). The helper
scripts/ci_build_test.ps1 automates the whole register / configure / build /
test flow and auto-detects the newest installed Visual Studio:
$env:O3DE_ENGINE_PATH = "C:\O3DE\26.05" # folder with scripts\o3de.bat
$env:DIORAMA_PROJECT = "C:\path\to\YourProject"
$env:GEM_PATH = "C:\path\to\o3de-diorama"
powershell -ExecutionPolicy Bypass -File C:\path\to\o3de-diorama\scripts\ci_build_test.ps1- Open your project in the O3DE Editor with the Diorama gem enabled.
- Create an entity and add the Sprite component.
- Assign a texture to
Config | Texture. - Optionally set an atlas sub-region under
Config | Atlas / UV Region(UV Min/UV Max, plusFlip Horizontal/Flip Vertical), a worldSize,Billboard, and a 2.5DLayering | Sort Offset.
The sprite renders in world space and is visible both in the editor viewport and at runtime.
To scaffold a fresh 2.5D project with the gem already enabled, register the bundled project template once and create a project from it:
<engine>/scripts/o3de.sh register --template-path /path/to/o3de-diorama/Templates/Diorama2DGame
<engine>/scripts/o3de.sh create-project --project-path /path/to/MyGame --template-name Diorama2DGameThe new project ships with Diorama enabled, 2.5D starter assets, and a
STARTING.md first-steps guide. See
How-To: Start a New 2.5D Game from the Template.
- Two-module split. A lightweight runtime client module and a separate Qt editor module. Shipped games carry only the runtime, with no Qt or AzToolsFramework dependency, so adding 2D costs little at runtime while authors still get full editor tooling.
- Integration over reinvention. Diorama builds on existing O3DE systems (transforms, prefabs, scripting, physics) instead of inventing parallel ones.
- A path designed to scale. Rendering goes through a batched Atom feature processor: sprites and tilemap tiles that share a texture collapse into one draw call, so a busy scene stays cheap.
- Deterministic when you want it. An opt-in fixed-step simulation clock with seeded randomness, full state snapshot/restore (with a verifiable state hash), and a per-frame input ring make the same inputs replay to the same result: the foundation for replays, training-mode rewind, and rollback netcode readiness, proven by a determinism test that runs in CI on every change.
For the full design with diagrams (module split, data model, persistence, and the render path) see Docs/architecture.md. In-depth references live under Docs/reference/: every Sprite and Tilemap parameter, and the typed bus API for scripts and agents.
The learning ladder is complete through the current feature set: the step-by-step how-to guides (full outline in Docs/examples-outline.md) run from Hello Sprite up through animation, atlases, tilemaps + autotiling, parallax, lighting, camera, particles, materials, post, audio, the project template, genre building blocks (fighting, bullet patterns, platforming, brawler, shmup), day/night, grid intelligence, and the deterministic simulation layer -- matching the feature table above.
A first sample showcase is the cartoon solar-system diorama
(DioramaSolarSystem): a layered 2.5D scene that exercises much of the stack at
once -- a setting sun, planets, a rabbit-marked moon, and a comet over
normal-mapped terrain, animated with a panning parallax camera, a particle
campfire and comet trail, flickering dynamic firelight, emissive glow, and a
twilight sky. It is authored offline by scripts/gen_diorama_solar_level.py from
procedurally generated art (scripts/gen_cartoon_*.py). It is an early effort; a
more ambitious flagship showcase is still a goal.
Still ahead
- A scene-to-image export API (render a Diorama scene to a PNG at any resolution), which doubles as a deterministic headless capture path.
- Toward
1.0: settle and freeze the bus API surface, and stand up an always-available build/test CI gate (the Windows host build is now verified; see the status note above).
Diorama follows Semantic Versioning. Before 1.0 the
version stays on the 0.x line, where minor (0.MINOR.0) bumps may include
breaking changes and patch (0.x.PATCH) bumps are fixes. Each release is an
annotated git tag (vMAJOR.MINOR.PATCH) and is recorded in
CHANGELOG.md. The version field in
gem.json tracks the current release. A stable API is the goal of the
eventual 1.0.
Diorama complements the engine and contributes fixes back upstream. If something in O3DE (Atom, AzCore, asset builders, and so on) is found broken or improvable while working on Diorama, it is flagged and contributed back to o3de/o3de rather than patched around.
See CONTRIBUTING.md for how to build, test, and submit changes, and CODE_OF_CONDUCT.md for the standards expected in project spaces.
Guidelines:
- Keep the runtime client module free of Qt and AzToolsFramework. Editor-only code lives in the editor module.
- Treat asset-sourced data as untrusted: validate and bound it in builders and at load. No unchecked sizes feed GPU buffers.
- No per-frame allocations in the render loop.
- Match the surrounding code style and the existing SPDX file headers.
- If you change the gem version or its dependencies, regenerate the SBOM
(
python3 scripts/gen_sbom.py) and commitsbom.spdx.json; CI rejects a stale SBOM.
Two workflows run in CI:
- lint (always on, GitHub-hosted): clang-format (pinned), SPDX headers,
whitespace/EOF hygiene, JSON manifest validation, and an SBOM freshness
check (
sbom.spdx.jsonmust matchscripts/gen_sbom.pyoutput). This is the gate every push and pull request must pass. Format C++ with clang-format 18.1.8 to match it (a newer local version may format differently). - build-test (opt-in, GitHub-hosted): compiles the gem through a host O3DE
project and runs the unit tests. Both legs run on free GitHub-hosted runners,
so untrusted PR code never touches local hardware. The Linux leg runs in a
Fedora container with the SDK baked in (built by the
ci-imageworkflow); the Windows leg installs the O3DE SDK onwindows-latestat runtime. It is opt-in: add theci:buildlabel (Windows) orci:build-linuxlabel (Linux) to a pull request, or trigger it manually. You can run the same checks locally withscripts/ci_build_test.sh(Linux) orscripts/ci_build_test.ps1(Windows). See Docs/ci-build-test.md.
Diorama vendors no third-party code; its dependencies are the O3DE engine and a
few O3DE gems. A minimal SPDX SBOM of that direct surface is at
sbom.spdx.json (regenerate with scripts/gen_sbom.py); the
transitive third-party tree is owned and documented by
O3DE. To report a vulnerability, see
SECURITY.md -- use GitHub's private vulnerability reporting, not a
public issue.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE), or
- MIT license (LICENSE-MIT)
at your option. Every source file carries SPDX-License-Identifier: Apache-2.0 OR MIT
declaring the same dual license.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

