Nodens
A C++23 framework for building interactive desktop applications quickly, with optional Dear
ImGui. Everything is a C++ module, so an app starts with import nodens; and links
one static library.
Writing an app
- An app inherits from
Nodens::Application, pushes one or more layers, and callsRun(). Every frame, each layer receivesOnEvent,OnUpdate(TimeStep)andOnImGuiRender(TimeStep). - Two examples ship with it:
circularwave3d(live 2D/3D plots with ImPlot and ImPlot3D) andplanetaryscan(the event bus and job system working together). - Tracy profiling is built in: add
ZoneScopedto any layer method. - CMake builds every dependency from source, so there is nothing to install by hand.
- 01 · Modules
Module-first
Public APIs are C++ module interfaces (
.cppm). Third-party headers stay in the global module fragment, so an app only ever writesimport nodens;. - 02 · Delivery
One static library
The framework builds into a single static library, so the final product is one portable executable.
- 03 · Platform
Linux, on purpose
The validated environment is Arch Linux, shared with Nyar through one dev container image. Native Windows and macOS builds are not supported.
Layer lifecycle
Every layer is a subclass of ILayer and overrides only the hooks it needs.
| Hook | When it runs | Use it for |
|---|---|---|
OnAttach | Once, when the layer is pushed onto the stack | Allocating resources, subscribing to events |
OnUpdate(ts) | Every frame, with the time since the last frame | Simulation and application logic |
OnImGuiRender(ts) | Every frame, between the start and end of the ImGui frame | Windows, plots and debug tools |
OnInputEvent(event) | When an input or window event reaches the layer, back to front | Handling input; set Handled to stop it travelling further |
OnDetach | Once, when the layer is removed | Releasing resources, unsubscribing |
Layers and overlays
The layer stack is one ordered list with a boundary in it. PushLayer inserts before the boundary and PushOverlay appends after it, so overlays such as the ImGui layer always update and draw after regular layers. The stack owns the layer pointers and deletes them on shutdown.
Two event paths
Events that come from the window and events your own code defines take different routes.
- Input and window events
Layer stack
They travel as one
std::variant, back to front through the layers, until one handles it. - Events you define
EventBus
Subscribers register typed handlers.
Dispatchcalls them at once, in subscription order, on the calling thread.QueueEventcopies the event into a double-buffered byte array thatFlush()drains once per frame on the main thread.
Event types
- 01 · Plain data
Events are POD
An event is a standard-layout, trivially copyable struct with a
static constexpr std::string_view Name. A C++ concept enforces it, so events can be copied straight into byte buffers. - 02 · No RTTI
Sequential type IDs
Each event type gets the next integer from an atomic counter the first time it is used. No RTTI and no hashing.
- 03 · Threads
Safe where it matters
Subscribe,UnsubscribeandQueueEventmay be called from any thread.DispatchandFlushbelong to the main thread.
Events and jobs together
- The job system runs work on a pool of
std::jthreadworkers, sized to the hardware thread count minus one so the main thread stays free. Submittakes any callable and returns astd::future. Workers sleep on a condition variable that understands stop tokens, so shutdown cancels cooperatively and joins on its own.- A job finishes by queueing an event. The next
Flush()hands the result to subscribers on the main thread, so no handler needs a lock of its own. - The
planetaryscanexample is this pattern with live plots.
Profiling with Tracy
- 01 · CPU
Zones and frames
Add
ZoneScopedto any function to see it in Tracy. The job queue depth is plotted on its own timeline. - 02 · Threads
Lock contention
The job queue mutex is a Tracy lockable, so contention between workers is visible as it happens.
- 03 · GPU
Vulkan timings
The Vulkan backend keeps per-frame Tracy contexts for GPU profiling next to the CPU view.
Where it comes from
Nodens began as a personal project for my undergraduate Computer Graphics course at UFMG. Its foundations follow the early episodes of The Cherno's Game Engine series, and earlier iterations powered several of my academic projects, including boids and galaxians. Versions of it also ran the numerical simulation applications from my research, which are on my portfolio.
Examples
Both ship in the repository under examples/.
| Example | Shows |
|---|---|
circularwave3d | The immediate-mode GUI, with live 2D and 3D plots from ImPlot and ImPlot3D |
planetaryscan | The event bus and job system together, with thread-safe data collection and a live scatter plot |
Get started
References
- The Cherno. (n.d.). Game engine series [Video playlist]. YouTube. http://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT
- Gregory, J. (2019). Game engine architecture (3rd ed.). CRC Press.
- Horton, I., & Van Weert, P. (2023). Beginning C++23: From beginner to pro. Apress. https://doi.org/10.1007/978-1-4842-9343-0