Table of contents
nxsand: A GPU grid for falling sand
nxsand is a falling-sand simulation for Switch with a desktop development target. Materials live in an integer grid. Simulation passes update that grid, and a separate rendering path turns material identifiers into the displayed image.
- Languages
- 7Code breakdown
- Documented components
- 2Explore modules
- Source documents
- 3Read the instructions
Repository languagesC++ 80.07%7
Shares calculated from GitHub Linguist bytes.
| Language | % | Bytes |
|---|---|---|
| C++ | 526,697 | |
| GLSL | 65,789 | |
| Python | 29,193 | |
| PowerShell | 21,830 | |
| Makefile | 10,750 | |
| Shell | 2,016 |
1 more languages
| Just | 0.23% | 1,491 |
1. Overview
1.1. Why it exists
Cellular automaton sand physics simulation engine running on libnx with touch and controller input.
1.2. Best fit
Interactive physics simulations on handheld console touchscreens.
2. Use cases
Falling-sand sandbox for Nintendo Switch homebrew, simulated on the GPU with OpenGL ES 3.0, with desktop builds.
- Paint materials on the grid
- PlayerGPU (OpenGL ES 3.0)
Touch, Joy-Con or mouse input becomes brush strokes (source/sim/brush_stroke.cpp) written into the grid by shaders/paint.frag; the X button opens the material ring (source/ui/material_wheel.cpp).
- Watch materials react
- PlayerGPU (OpenGL ES 3.0)
A Margolus cellular automaton runs on ping-pong GL_R8UI textures in shaders/sim.frag or sim.comp, with reactions such as lava plus water giving stone in shaders/sim_common.glsl and the sim_rules_body.glsl it includes.
- Save and load worlds
- Player
source/save/save.cpp stores up to three slots as JSON with base64 grids in sdmc:/switch/nxsand/ (./nxsand_save/ on desktop); Y or F5 quick-saves.
- Tune performance settings
- PlayerGPU (OpenGL ES 3.0)
settings.json and the Engine menu set sim resolution, substeps, active tiles, fragment or compute backend and the Battery Saver, Balanced or Quality presets.
- Import a Powder Toy stamp
- Homebrew developer
nx::importTptStampJson in source/save/tpt_stamp_import.cpp maps The Powder Toy element IDs to NXSand materials and scales the stamp into the grid.
- Build and test the NRO
- Homebrew developer
make builds build/NXSand.nro with devkitPro; make desktop and make test build the SDL2 desktop version and the CPU reference tests in tests/; CI does the same in .github/workflows/native-nro.yml.
2.1. Scenario: Build NXSand and play a first world
- Install devkitPro, then run dkp-pacman -S switch-dev switch-sdl2 switch-mesa switch-glm switch-freetype switch-harfbuzz.
- Run make at the repository root to get build/NXSand.nro.
- Copy it to sdmc:/switch/NXSand.nro and launch it from the Homebrew Menu.
- Press X to pick Lava, paint with A, then switch to Water and pour it on the lava to see stone and smoke form.
- Press Y to quick-save the world into sdmc:/switch/nxsand/.
Files readREADME.mddocs/PHYSICS.mddocs/SETTINGS.mddocs/TPT_IMPORT.mdMakefileshaders/sim_common.glslshaders/paint.fragsource/save/save.cppsource/save/tpt_stamp_import.cppsource/sim/brush_stroke.cppsource/ui/material_wheel.cppsource/game/engine_settings.cpp.github/workflows/native-nro.yml
3. Getting started
These commands come from the repository documentation. They were not run during this review.
dkp-pacman -Q libnx switch-sdl2
dkp-pacman -Syu # pull newer libnx / switch-sdl2 when availabledkp-pacman -S switch-dev switch-sdl2 switch-mesa switch-glm switch-freetype switch-harfbuzzmakemake desktop
./build/NXSand
make test # CPU reference tests, no GPUpacman -S --needed mingw-w64-x86_64-gcc mingw-w64-x86_64-make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-freetype mingw-w64-x86_64-angleproject4. Repository references
Excerpts from the repository’s guides, in their original language. Links open the inspected source revision.
Prerequisites and first run
4.1. Install on Switch (players)
README.md
You need a homebrew-ready Switch (setup guide).
- Get NXSand.nro — build it yourself (below), download the latest NXSand-switch zip from GitHub Actions (switch/NXSand.nro inside; optional NXSand.nsp forwarder when CI has SWITCH_PROD_KEYS), or use a release tag (Switch + Linux + Windows portable zips).
- Copy to the SD card: sdmc:/switch/NXSand.nro (unzip the artifact at the card root, or copy switch/NXSand.nro into switch/).
- Launch from the Homebrew Menu. Saves live in sdmc:/switch/nxsand/. Legacy sdmc:/switch/nxengine/ data is migrated on first launch when possible. Full install notes: docs/INSTALL.md.
4.2. 1. Install devkitPro
README.md
| OS | Install |
|---|---|
| Windows | Download and run the installer from devkitPro installer releases. Use the MSYS2 shortcut it adds (e.g. “devkitPro MSYS2”) so make and dkp-pacman are on your PATH. |
| Linux / macOS | Follow Getting Started on devkitpro.org. |
| Set DEVKITPRO if your environment does not (the installer usually does). |
4.3. 2. Install Switch libraries
README.md
4.4. Installing NXSand on Nintendo Switch
docs/INSTALL.md
4.5. Install on Switch
docs/INSTALL.md
- Copy NXSand.nro to sdmc:/switch/NXSand.nro (required).
- Install NXSand.nsp with your CFW tool (Tinfoil, DBI, Goldleaf, etc.).
- Launch NXSand from the Home menu. If the forwarder fails, launch the .nro from the homebrew menu instead. Forwarders need a CFW setup that allows unsigned NSP installs. Keep prod.keys private; never commit them.
Operations and troubleshooting
4.6. Troubleshooting
docs/INSTALL.md
- The app file must be sdmc:/switch/NXSand.nro, not inside the save folder.
- Build and deploy: powershell -File scripts\build-native.ps1 → copy dist/switch/NXSand.nro to sdmc:/switch/NXSand.nro. Run powershell -File scripts\verify-nro.ps1 before copying.
- Confirm the deployed NRO: delete sdmc:/switch/nxsand/launch.log, launch once, and check the latest block includes build: switch-sim-log-v11, switch: sim shader=sim.frag, romfs: sim.frag=… sim_rules_body.glsl=…, sim shader path: shaders/sim.frag, switch: default sim backend=fragment, and no preferring compute sim. Recopy from a fresh build-native.ps1 build if you still see sim_switch paths.
- If the app opens then exits, NXSand shows a fatal text screen and writes sdmc:/switch/nxsand/launch.log when possible.
4.7. When to update
docs/diagrams/README.md
- sim-pipeline.mmd, sim-margolus-step.mmd: sim/render pass wiring, compute backend, active-tile scissor rules.
- material-reactions.mmd, reaction-lava-water-quench.puml: shaders/sim.frag / sim_common.glsl reaction branches.
- save-load-flow.puml: source/save/save.cpp JSON fields or paths.
- game-scenes.mmd, brush-input-flow.mmd: menu flow, input mapping, PlayRegion.
- core-runtime-classes.puml, source-modules.mmd: new top-level modules or ownership changes.
Source documentation
3 documents read from 3 candidates.
5. Features and components 2
- Simulation state
- SimState holds dimensions, tick state and brush parameters. SimPipeline owns two textures and framebuffers and exposes paint, step, readback and upload operations. Ping-pong storage keeps the previous grid available while producing the next.
- Backend and tests
- The fragment path uses GL_R8UI; the compute path uses GL_R32UI for imageStore. Active-tile tracking and fallback state are exposed in the pipeline. The Makefile separates CPU tests, GPU tests and the Switch application build.
6. Architecture
Expand a component to follow its connections, or explore the full diagram.
Brush commands
Integer material grid
- From Brush commands
- To Simulation pass
- From Next grid texture
Simulation pass
Next grid texture
Render pipeline
- From Next grid texture
Mermaid Interactive diagram
System overview of the components and connections verified in the repository.

flowchart LR
node_player(("Player"))
subgraph group_runtime["Runtime"]
node_entry["Platform entry<br/>[main.cpp]"]
node_app["App frame loop<br/>[app.cpp]"]
node_game_settings["Game settings<br/>[game_settings.hpp]"]
node_engine_settings["Engine settings"]
end
subgraph group_simulation["Simulation"]
node_sim_state["Simulation state<br/>[sim_state.hpp]"]
node_materials["Materials<br/>[materials.hpp]"]
node_physics["Physics parameters<br/>[physics_params.hpp]"]
node_sim_pipeline["GPU simulation<br/>[sim_pipeline.cpp]"]
node_sim_backend["Simulation backend<br/>[sim_backend.hpp]"]
end
subgraph group_presentation["Presentation"]
node_render["World renderer"]
node_shader_program["Shader programs<br/>[shader_program.cpp]"]
node_font["Font atlas<br/>[font_atlas.cpp]"]
node_audio["Audio<br/>[tone_audio.cpp]"]
end
subgraph group_interaction["Interaction"]
node_input["Input handling<br/>[input.cpp]"]
node_menu["Menus<br/>[menu.cpp]"]
node_layout["UI layout<br/>[layout.hpp]"]
node_theme["UI theme<br/>[theme.hpp]"]
end
subgraph group_persistence["Persistence"]
node_save["World saves<br/>[save.cpp]"]
node_save_paths["Save storage<br/>[save_paths.cpp]"]
node_settings_io["Settings storage<br/>[settings_io.cpp]"]
end
node_player -->|"launches"| node_entry
node_entry -->|"starts"| node_app
node_app -->|"renders"| node_render
node_app -->|"reads"| node_input
node_app -->|"coordinates"| node_menu
node_app -->|"applies settings"| node_engine_settings
node_app -->|"uses settings"| node_game_settings
node_sim_pipeline -->|"updates grid"| node_sim_state
node_sim_pipeline -->|"simulates materials"| node_materials
node_sim_pipeline -->|"uses parameters"| node_physics
node_sim_pipeline -->|"selects backend"| node_sim_backend
node_render -->|"uses shaders"| node_shader_program
node_render -->|"draws grid"| node_sim_state
node_render -->|"colors materials"| node_materials
node_menu -->|"edits settings"| node_engine_settings
node_menu -->|"uses text"| node_font
node_menu -->|"draws interface"| node_render
node_layout -->|"uses styles"| node_theme
node_input -->|"maps controls"| node_layout
node_audio -->|"reads preferences"| node_game_settings
node_save -.->|"uses storage"| node_save_paths
node_settings_io -->|"uses storage"| node_save_paths
group_runtime --> group_simulation
group_runtime --> group_persistence
class node_entry,node_app,node_game_settings,node_engine_settings toneBlue
class node_sim_state,node_materials,node_physics,node_sim_pipeline,node_sim_backend toneAmber
class node_render,node_shader_program,node_font,node_audio toneMint
class node_input,node_menu,node_layout,node_theme toneRose
class node_save,node_save_paths,node_settings_io,node_player toneIndigo
A Nintendo Switch falling-sand sandbox with desktop builds for iteration. Its frame loop coordinates GPU simulation, rendering, input, menus, persistence and settings around a live material grid.
- Components
- 21
- Connections
- 28
- Evidence-backed links
- 15/28
Text version of the diagram
The diagram merges parallel links between groups, so it shows 24 of 28. Full list:
- Player → Platform entry (launches)
- Platform entry → App frame loop (starts)
- App frame loop → GPU simulation (ticks)
- App frame loop → World renderer (renders)
- App frame loop → Input handling (reads)
- App frame loop → Menus (coordinates)
- App frame loop → World saves (requests saves)
- App frame loop → Settings storage (loads settings)
- App frame loop → Engine settings (applies settings)
- App frame loop → Game settings (uses settings)
- App frame loop → Simulation state (holds state)
- App frame loop → Physics parameters (holds parameters)
- GPU simulation → Simulation state (updates grid)
- GPU simulation → Materials (simulates materials)
- GPU simulation → Physics parameters (uses parameters)
- GPU simulation → Simulation backend (selects backend)
- World renderer → Shader programs (uses shaders)
- World renderer → Simulation state (draws grid)
- World renderer → Materials (colors materials)
- Menus → Engine settings (edits settings)
- Menus → Font atlas (uses text)
- Menus → World renderer (draws interface)
- UI layout → UI theme (uses styles)
- Input handling → UI layout (maps controls)
- Audio → Game settings (reads preferences)
- World saves → Save storage (uses storage)
- Settings storage → Save storage (uses storage)
- Engine settings → Settings storage (persists preferences)
Follow one path through the code
Follow paintDisk into grid modification, then step into the selected backend. readTexture identifies the current grid for rendering. Save readback must also account for OpenGL row orientation rather than copying a displayed screenshot.
7. Technical dossier
Built from the source at commit af70da3 (138 files). Each item comes from a pattern match in the code, so open the linked file before relying on it.
7.1. Layers and modules
Folders are sorted into layers by name, from the user interface down to data access. Because the sorting uses names only, the diagram says nothing about calls between layers.
Mermaid Layered view
flowchart TB
subgraph L_presentation["Presentation"]
direction TB
presentation_0["ui<br/>30 files"]
end
subgraph L_application["Application services"]
direction TB
application_0["scripts<br/>18 files"]
application_1["gpu<br/>4 files"]
end
subgraph L_domain["Domain and business rules"]
direction TB
domain_0["game<br/>8 files"]
end
subgraph L_infrastructure["Data and infrastructure"]
direction TB
infrastructure_0["platform/audio<br/>10 files"]
infrastructure_1["platform/input<br/>7 files"]
infrastructure_2["platform<br/>4 files"]
infrastructure_3["gpu<br/>2 files"]
end
subgraph L_core["Core source"]
direction TB
core_0["gpu<br/>13 files"]
core_1["save<br/>13 files"]
core_2["sim<br/>13 files"]
core_3["shaders<br/>3 files"]
core_more["+2"]
end
subgraph L_tests["Tests"]
direction TB
tests_0["tests<br/>21 files"]
end
subgraph L_hosting["Build, CI and hosting"]
direction TB
hosting_0[".github<br/>4 files"]
hosting_1["(root)<br/>2 files"]
end
L_presentation ~~~ L_application
L_application ~~~ L_domain
L_domain ~~~ L_infrastructure
L_infrastructure ~~~ L_core
L_core ~~~ L_tests
L_core ~~~ L_hosting
| Module | Layer | Files | Types | Extensions |
|---|---|---|---|---|
ui | Presentation | 30 | 9 | .hpp .cpp |
gpu | Core source | 19 | 17 | .hpp .cpp |
scripts | Application services | 18 | 0 | .ps1 .py .sh |
save | Core source | 13 | 1 | .hpp .cpp |
sim | Core source | 13 | 7 | .hpp .cpp |
platform/audio | Data and infrastructure | 10 | 3 | .cpp .hpp |
game | Domain and business rules | 8 | 10 | .cpp .hpp |
platform/input | Data and infrastructure | 7 | 4 | .hpp .cpp |
platform | Data and infrastructure | 4 | 0 | .cpp .hpp |
shaders | Core source | 3 | 0 | .glsl |
third_party/glad | Core source | 3 | 0 | .h .c |
third_party/nlohmann | Core source | 1 | 0 | .hpp |
7.2. C4 model
From system context to code declarations. Each level reflects the available source evidence.
UML 1 · System context
The widest C4 view: who uses the project and which outside systems and data stores it relies on. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Context>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System(SYS, "nxsand", "nxsand is a falling-sand simulation for Switch with a desktop develop…")
System_Ext(E0, "Kroki")
Rel(P_cli, SYS, "Uses")
Rel(SYS, E0, "Calls")
@enduml
UML 2 · Containers
One level closer: the parts that run separately inside the project, their main language and how calls flow between them. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Container>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System_Boundary(SYS, "nxsand") {
Container(APP, "nxsand", "C++")
}
System_Ext(E0, "Kroki")
Rel(P_cli, APP, "Uses")
Rel(APP, E0, "Calls")
@enduml
UML 3 · Components and layers
Modules grouped by source-layer classification. Boundaries show organization, not runtime isolation or call relationships. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Component>
LAYOUT_TOP_DOWN()
System_Boundary(SYS, "nxsand") {
Boundary(L0, "Presentation", "layer") {
Component(M0_0, "ui", "C++", "30 files")
}
Boundary(L1, "Application services", "layer") {
Component(M1_0, "scripts", ".ps1, Python, .sh", "18 files")
Component(M1_1, "gpu", "C++", "4 files")
}
Boundary(L2, "Domain and business rules", "layer") {
Component(M2_0, "game", "C++", "8 files")
}
Boundary(L3, "Data and infrastructure", "layer") {
Component(M3_0, "platform/audio", "C++", "10 files")
Component(M3_1, "platform/input", "C++", "7 files")
Component(M3_2, "platform", "C++", "4 files")
Component(M3_3, "gpu", "C++", "2 files")
}
Boundary(L4, "Core source", "layer") {
Component(M4_0, "gpu", "C++", "13 files")
Component(M4_1, "save", "C++", "13 files")
Component(M4_2, "sim", "C++", "13 files")
Component(M4_3, "shaders", ".glsl", "3 files")
Component(M4_4, "third_party/glad", "C/C++, C", "3 files")
Component(M4_5, "third_party/nlohmann", "C++", "1 files")
}
Boundary(L5, "Tests", "layer") {
Component(M5_0, "tests", "C++", "21 files")
}
Boundary(L6, "Build, CI and hosting", "layer") {
Component(M6_0, ".github", ".yml, .json", "4 files")
Component(M6_1, "(root)", "Makefile, justfile", "2 files")
}
}
@enduml
UML 4 · Code
Selected declarations, fields and methods grouped by source file. No inheritance or call edges are inferred. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
top to bottom direction
hide empty members
package "nxsand" {
package "source/game/app.hpp" as F0 {
class "App" as T0_0 {
shutdown()
frame()
setSimGridSize()
applyRuntimeSettings()
applyRuntimeSettingsLight()
applyRuntimeSettingsHeavy()
}
}
package "source/gpu/active_tiles.hpp" as F1 {
class "ActiveTileMap" as T1_0 {
reset()
wakeAll()
sleepAll()
wakeFromGridTopDown()
wakeBoundsPx()
rememberBounds()
}
class "ActiveTileRun" as T1_1 {
std
}
}
package "source/gpu/perf_stats.hpp" as F2 {
class "PerfStats" as T2_0 {
beginFrame()
beginSim()
endSim()
beginPaint()
endPaint()
beginWorldRender()
}
}
package "source/gpu/font_atlas.hpp" as F3 {
class "FontAtlas" as T3_0 {
isReady()
shutdown()
invalidateGlTexture()
resetGlResources()
textWidth()
drawText()
}
}
package "source/gpu/shader_program.hpp" as F4 {
class "ShaderProgram" as T4_0 {
loadFromFiles()
loadComputeFromFile()
use()
uniformLocation()
readFile()
compile()
}
}
package "source/platform/input/menu_repeat.hpp" as F5 {
class "MenuRepeatState" as T5_0 {
reset()
tick()
tickAxis()
}
}
package "source/gpu/shader_program.cpp" as F6 {
class "SwitchLinkHeartbeat" as T6_0 {
start()
finish()
}
}
package "source/ui/menu.hpp" as F7 {
class "MenuLayout" as T7_0 {
s
panelX
panelY
panelW
panelH
rowH
listY0
titleY
}
}
package "source/sim/physics_gpu.hpp" as F8 {
class "PhysicsParamsGPU" as T8_0 {
fire_speed
fire_smokeRate
fire_ignitePlant
fire_igniteOil
fire_spreadRate
smoke_fadeRate
smoke_driftRate
water_flowRate
}
}
package "source/gpu/shader_cache.cpp" as F9 {
class "ShaderCacheHeader" as T9_0 {
magic
version
keyHash
binaryFormat
payloadSize
}
}
package "source/gpu/sim_pipeline.hpp" as F10 {
class "SimPassRect" as T10_0 {
simShader
computeShader
paintShader
activeTiles
}
}
package "source/gpu/render_pipeline.hpp" as F11 {
class "UiVertex" as T11_0 {
std
bloomBrightShader
bloomBlurShader
bloomCompositeShader
}
}
package "source/sim/brush_stroke.hpp" as F12 {
class "BrushDab" as T12_0 {
x
y
std
}
class "BrushStrokePlan" as T12_1 {
std
}
}
package "source/gpu/font_atlas.cpp" as F13 {
class "FtLibrary" as T13_0 {
std
true
}
}
package "source/sim/physics_settings.hpp" as F14 {
class "ParamSpec" as T14_0 {
id
label
}
}
package "source/ui/layout.hpp" as F15 {
class "PlayRegion" as T15_0 {
scaleX
scaleY
}
}
package "source/game/game_settings.hpp" as F16 {
class "DebugSettings" as T16_0 {
std
}
class "DisplaySettings" as T16_1 {
std
}
class "GameSettings" as T16_2 {
std
}
}
}
@enduml
7.3. Data flow
The calls from the GitDiagram graph, in the order you meet them from the entry points. GitDiagram generates the graph with AI; the file paths it cites were checked against the repository tree.
Mermaid Sequence diagram
sequenceDiagram
autonumber
participant P0 as Audio
participant P1 as Game settings
participant P2 as Player
participant P3 as Platform entry
participant P4 as App frame loop
participant P5 as GPU simulation
participant P6 as World renderer
participant P7 as Input handling
participant P8 as Menus
participant P9 as World saves
participant P10 as Settings storage
participant P11 as Engine settings
participant P12 as Simulation state
participant P13 as Physics parameters
participant P14 as Materials
P0->>P1: reads preferences
P2->>P3: launches
P3->>P4: starts
P4->>P5: ticks
P4->>P6: renders
P4->>P7: reads
P4->>P8: coordinates
P4->>P9: requests saves
P4->>P10: loads settings
P4->>P11: applies settings
P4->>P1: uses settings
P4->>P12: holds state
P4->>P13: holds parameters
P5->>P12: updates grid
P5->>P14: simulates materials
P5->>P13: uses parameters
Steps as text (+4)
- Audio to Game settings reads preferences
- Player to Platform entry launches
- Platform entry to App frame loop starts
- App frame loop to GPU simulation ticks
- App frame loop to World renderer renders
- App frame loop to Input handling reads
- App frame loop to Menus coordinates
- App frame loop to World saves requests saves
- App frame loop to Settings storage loads settings
- App frame loop to Engine settings applies settings
- App frame loop to Game settings uses settings
- App frame loop to Simulation state holds state
- App frame loop to Physics parameters holds parameters
- GPU simulation to Simulation state updates grid
- GPU simulation to Materials simulates materials
- GPU simulation to Physics parameters uses parameters
- GPU simulation to Simulation backend selects backend
- World renderer to Shader programs uses shaders
- World renderer to Simulation state draws grid
- World renderer to Materials colors materials
- Input handling to UI layout maps controls
- Menus to Engine settings edits settings
- Menus to Font atlas uses text
- Menus to World renderer draws interface
7.4. Public interface
Outside code can use everything listed here. The project also declares 51 public types.
Command line 9
Entry points
source/platform/main.cppentry file
7.5. Private interface
These names are internal by the language’s conventions: a leading underscore, private, static or pub(crate).
- static (file-local) function
- 74
- anonymous namespace
- 19
- underscore helper
- 9
_is_darkscripts/crop_icon.py:12_is_letterboxscripts/crop_icon.py:16_artwork_bboxscripts/crop_icon.py:21getenvEnabledsource/game/app.cpp:76bootLogStagesource/game/app.cpp:83simBackendNamesource/game/app.cpp:93namespace { }source/game/app.cpp:45namespace { }source/gpu/font_atlas.cpp:17namespace { }source/gpu/gl_loader.cpp:5
7.6. Services
Service 2
RenderPipelineshutdown, buildPaletteTexture, drawSimulation and 32 moreSimPipelineshutdown, clearAll, paintDisk and 24 more
7.7. Entities and data model 36
Mermaid Entity class diagram
classDiagram
direction LR
class E0["MenuLayout"] {
+float s
+float panelX
+float panelY
+float panelW
+float panelH
+float rowH
}
class E1["PhysicsParamsGPU"] {
+float fire_speed
+float fire_smokeRate
+float fire_ignitePlant
+float fire_igniteOil
+float fire_spreadRate
+float smoke_fadeRate
}
class E2["ShaderCacheHeader"] {
+char magic
+uint32_t version
+uint64_t keyHash
+uint32_t binaryFormat
+uint32_t payloadSize
}
class E3["SimPassRect"] {
+ShaderProgram simShader
+ShaderProgram computeShader
+ShaderProgram paintShader
+ActiveTileMap activeTiles
}
class E4["UiVertex"] {
+:vector std
+ShaderProgram bloomBrightShader
+ShaderProgram bloomBlurShader
+ShaderProgram bloomCompositeShader
}
class E5["BrushDab"] {
+int x
+int y
+:vector std
}
class E6["FtLibrary"] {
+:ifstream std
+return true
}
class E7["ParamSpec"] {
+char id
+char label
}
class E8["PlayRegion"] {
+int scaleX
+int scaleY
}
class E9["DebugSettings"] {
+:pair std
}
An arrow links a field to the entity its type names.
| Entity | Kind | First fields |
|---|---|---|
MenuLayout | Data struct | s, panelX, panelY, panelW, panelH, rowH, listY0, titleY |
PhysicsParamsGPU | Data struct | fire_speed, fire_smokeRate, fire_ignitePlant, fire_igniteOil, fire_spreadRate, smoke_fadeRate, smoke_driftRate, water_flowRate |
ShaderCacheHeader | Data struct | magic, version, keyHash, binaryFormat, payloadSize |
SimPassRect | Data struct | simShader, computeShader, paintShader, activeTiles |
UiVertex | Data struct | std, bloomBrightShader, bloomBlurShader, bloomCompositeShader |
BrushDab | Data struct | x, y, std |
FtLibrary | Data struct | std, true |
ParamSpec | Data struct | id, label |
Show 6 more entities
PlayRegion | Data struct | scaleX, scaleY |
|---|---|---|
DebugSettings | Data struct | std |
DisplaySettings | Data struct | std |
GameSettings | Data struct | std |
ActiveTileRun | Data struct | std |
BrushStrokePlan | Data struct | std |
7.8. Business logic
Once UI code and the services and entities above are set aside, these classes have the most methods.
AppDomain and business rules, 37 methodsshutdown, frame, setSimGridSize, applyRuntimeSettings, applyRuntimeSettingsLight, applyRuntimeSettingsHeavyActiveTileMapCore source, 23 methodsreset, wakeAll, sleepAll, wakeFromGridTopDown, wakeBoundsPx, rememberBoundsPerfStatsCore source, 12 methodsbeginFrame, beginSim, endSim, beginPaint, endPaint, beginWorldRenderFontAtlasCore source, 11 methodsisReady, shutdown, invalidateGlTexture, resetGlResources, textWidth, drawTextShaderProgramCore source, 8 methodsloadFromFiles, loadComputeFromFile, use, uniformLocation, readFile, compileMenuRepeatStateData and infrastructure, 3 methodsreset, tick, tickAxisSwitchLinkHeartbeatCore source, 2 methodsstart, finish
7.9. External integrations
HTTP clients
Hosts named in the code
kroki.io1 filesscripts/regenerate-diagrams.py:26
7.10. Stack and dependencies
External dependencies
7.11. CI/CD pipeline
3 GitHub Actions workflows, 11 jobs. Each box is a job; double-bordered boxes publish or deploy, and arrows follow needs.
Mermaid Pipeline flow
flowchart LR
subgraph wf0["Documentation"]
direction LR
wf0_build[["build<br/>ubuntu-latest"]]
wf0_deploy[["deploy<br/>ubuntu-latest"]]
end
subgraph wf1["Build NXSand.nro"]
direction LR
wf1_unit_tests["CPU unit tests<br/>ubuntu-latest"]
wf1_switch[["Switch NRO<br/>ubuntu-latest"]]
wf1_desktop["Desktop sanity build (Linux)<br/>ubuntu-latest"]
wf1_desktop_windows["Desktop sanity build (Windows)<br/>windows-latest"]
end
subgraph wf2["Release NXSand"]
direction LR
wf2_unit_tests["CPU unit tests<br/>ubuntu-latest"]
wf2_build_switch[["Package Switch NRO<br/>ubuntu-latest"]]
wf2_build_linux[["Package Linux portable<br/>ubuntu-latest"]]
wf2_build_windows[["Package Windows portable<br/>windows-latest"]]
wf2_publish[["Publish GitHub release<br/>ubuntu-latest"]]
end
t0(["push · manual run"])
t1(["push · pull request · manual run"])
t2(["manual run"])
d0[("GitHub Pages")]
d1[("GitHub Releases")]
t0 --> wf0_build
wf0_build --> wf0_deploy
wf0_deploy -.-> d0
t1 --> wf1_unit_tests
wf1_unit_tests --> wf1_switch
wf1_unit_tests --> wf1_desktop
wf1_unit_tests --> wf1_desktop_windows
t2 --> wf2_unit_tests
wf2_unit_tests --> wf2_build_switch
wf2_unit_tests --> wf2_build_linux
wf2_unit_tests --> wf2_build_windows
wf2_build_switch --> wf2_publish
wf2_build_linux --> wf2_publish
wf2_build_windows --> wf2_publish
wf2_publish -.-> d1
| Workflow | Triggers | Jobs | Publishes to |
|---|---|---|---|
Documentation.github/workflows/docs.yml | push, manual run |
| GitHub Pages |
Build NXSand.nro.github/workflows/native-nro.yml | push, pull request, manual run |
| — |
Release NXSand.github/workflows/release.yml | manual run |
| GitHub Releases |
7.12. Deployment
Mermaid Deployment diagram
flowchart LR
repo[("GitHub repository")]
ci[["GitHub Actions<br/>3 workflows"]]
pub_GitHub_Pages[("GitHub Pages")]
pub_GitHub_Releases[("GitHub Releases")]
host_MkDocs_site("Documentation site (MkDocs)<br/>mkdocs.yml")
repo -->|"push or pull request"| ci
ci -->|"publishes"| pub_GitHub_Pages
ci -->|"publishes"| pub_GitHub_Releases
ci -.->|"configured"| host_MkDocs_site
Where it runs
Published to
7.13. Security
Controls
Environment variables 20
The page lists names without their values and underlines the ones that look like secrets.
NXENGINE_BOOT_LOG NXENGINE_SIM_H NXENGINE_SIM_W NXSAND_BOOT_LOG NXSAND_SIM_H NXSAND_SIM_W MINGW_PREFIX NXENGINE_DEBUG_GRID NXENGINE_DISABLE_AUDIO NXENGINE_LAUNCH_LOG NXENGINE_PALETTE_MODE NXENGINE_SWITCH_SWAP_FACE_XY NXENGINE_VERBOSE_LAUNCH_LOG NXSAND_DEBUG_GRID NXSAND_DISABLE_AUDIO NXSAND_FONT_PATH
7.14. Testing
- Test files
- 21
- Source files
- 129
- Unit tests
- 21
No measured coverage report in the analyzed sources.
Where the tests are
tests 21
Nothing found for: Database, Monitoring.
8. Types and file structure
53 declared types, with 12 in the base-relationship overview.
Type diagram
classDiagram
direction TB
class t7ba1fb6b5b9e["SimPassRect"] {
<<struct>>
}
class t658fbe1e623e["SimPipeline"] {
SimPipeline()
init()
shutdown()
clearAll()
}
class tb8626ee066b2["SimState"] {
<<struct>>
}
class tfdb610a7db47["SimCompileAudioGuard"] {
<<struct>>
SimCompileAudioGuard()
}
class ta050db13a10c["App"] {
init()
shutdown()
frame()
setSimGridSize()
}
class tf962c5d4ce57["PerformanceSettings"] {
<<struct>>
}
class tbdcb989e9dca["VisualSettings"] {
<<struct>>
}
class tbb5d68fb65da["ControlSettings"] {
<<struct>>
}
class t480673c07c2c["AudioSettings"] {
<<struct>>
}
class t28451b24c3ad["AccessibilitySettings"] {
<<struct>>
}
class t45aedf66edc0["DebugSettings"] {
<<struct>>
}
class t4b3cad79f762["DisplaySettings"] {
<<struct>>
}
Unresolved bases remain labelled; full signatures are not reconstructed.
9. Reference
135 / 135 eligible files read (240 in the tree); 0 oversized, 0 failed reads and 5 syntax diagnostics.
What the scan covers
Known dependency/build folders, credential files and binaries are excluded. The scan covers READMEs, recognized manifests and parser-supported source files.
Linguist statistics cover the whole repository and may include other languages.
11. Files to read 3
12. Glossary
Repository
The versioned source files, configuration, and history of a project.
README
The repository’s entry document, usually covering its purpose and initial setup.
Dependency
A library, tool, or service that another component needs to build or run.
Volume and bind mount
A volume is storage managed by the container platform; a bind mount exposes a chosen host path inside a container.
CLI
A text interface in which commands and arguments select operations. Exit codes let scripts detect success or failure.
Build
The steps that turn source files and dependencies into a runnable program or distributable artifact.
Continuous integration
Automated checks that run when source changes, such as building, testing, and linting. Passing checks cover only what those checks exercise.
GPU
A processor designed for parallel workloads, including graphics rendering and many numerical operations.
Shader
A program executed at a stage of a graphics or compute pipeline, for example to transform vertices or calculate pixel colors.
Backend
An implementation behind a shared interface. Depending on context, it can mean a server-side service or a graphics, input, or storage implementation.
13. References
- antoinebou12. (2026, November 5). nxsand. [Software repository] github.com.
- antoinebou12. (2026, November 5). nxsand: README. [Repository documentation] github.com.
- antoinebou12. (2026, November 5). source/gpu/sim_pipeline.hpp. [Source file] github.com.
- antoinebou12. (2026, November 5). source/sim/sim_state.hpp. [Source file] github.com.
- antoinebou12. (2026, November 5). Makefile. [Source file] github.com.
- Boucher, A. (2026, October 23). nxsand: A GPU grid for falling sand. [Project article] antoineboucher.info.
Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.