What to do when you face problems
C++ can be intimidating, and the error messages are famously dramatic. Here is the calm order of operations when something breaks, along with a recipe for the most common Umfeld bugs.
1. Read the first error, not the last
Compilers cascade. The first error in the output is almost always the real one; the rest are fallout. Scroll up.
2. Identify the bug (The First-Bug Recipe)
Most Umfeld bugs have the same shape. Check this list before you spend an hour debugging:
| Symptom | Most likely cause |
|---|---|
Segfault on first ImGui::Begin |
register_library(&imgui) was called in settings() instead of setup(). |
| ImGui panel draws under your scene | Vertex batch flush ordering — call flush() before begin_frame(). |
| Trails accumulate on resize | Using rect(0,0,w,h) to clear instead of background(c). |
| Panel opens off-screen (tiling WMs) | ImGui default pos is (0,0); set SetNextWindowPos with ImGuiCond_FirstUseEver. |
| Audio works, then crashes under load | malloc or new inside audioEvent — pre-allocate everything in setup(). |
undefined reference to vtable |
Added a new file but forgot to rerun cmake -B build. |
LSP/clangd says 'X' file not found |
No compile_commands.json — set CMAKE_EXPORT_COMPILE_COMMANDS ON. |
'frameRate' cannot be used as a function |
frameRate is a float variable, not a function. Assign: frameRate = 60; |
width/height math gives weird results |
Both are float, not int. Cast explicitly if integer logic is needed. |
| Built-in font draws garbage / wrong glyphs | Default font not loaded. Call textFont(loadFont("path.ttf", 32)) first. |
g->box() corrupts FBO or draws nothing |
3D PGraphics calls can be unstable on FBOs. Do manual perspective math or verify FBO state. |
| Build picks wrong OpenGL version / blank window | size(...) runtime call doesn’t match UMFELD_OPENGL_VERSION cmake variable. |
Common fixes by file:
application.cpp: If you addedaudio()orregister_library, make sure they are in the right phase (settings()forsize()+audio();setup()forregister_library).CMakeLists.txt: Added a new.cpp? You must reruncmake -B build. CMake’sGLOBevaluates at configure time, not build time, so new files remain invisible to the compiler until you reconfigure.audioEvent(): Nonew, nomalloc, nostd::vector::resize, no string concatenation. Pre-allocate everything insetup().- Any
.cppcallingImGui::*: Make sure the call site is betweenimgui_library.begin_frame()andimgui_library.end_frame(). - Any
.cppusingPGraphics(FBOs): Make sure everybeginDraw()has a matchingendDraw(). Missing pairs will corrupt the FBO and bleed into the main screen.
Why these bugs happen
Understanding the why behind common Umfeld bugs makes them easier to avoid:
-
Audio callback rules: The
audioEvent()function runs on a dedicated background thread (via SDL’s audio system). This means it is highly sensitive to anything that blocks or allocates memory. Usingnew,malloc,std::vector::resize, orstd::stringconcatenation inside this callback can cause silent buffer underruns or crashes under load. The fix is always the same: pre-allocate yourstd::vector<float>buffers and any other resources insetup(). -
The CMake GLOB trap: When you add a new
.cppfile and get an “undefined reference to vtable” error, it is because CMake’sfile(GLOB ...)only scans the directory at configure time (when you runcmake -B build), not at build time (when you runcmake --build build). Your new file is simply invisible to the compiler until you reconfigure. -
ImGui and vertex batching: If an ImGui panel draws under your scene, it is because Umfeld batches drawing calls (like
line()andcircle()) and flushes them at the end ofdraw(). Calling the free functionflush()right beforeimgui_library.begin_frame()forces the batched vertices to render first, so the UI sits on top. -
Tiling window managers: ImGui’s default window position of
(0,0)can place panels completely off-screen on tiling window managers like i3, sway, or Hyprland. Fix this withImGui::SetNextWindowPos({100, 100}, ImGuiCond_FirstUseEver)so the window starts in-bounds but can still be dragged afterward.
3. Rebuild from a clean slate
If you just added new .cpp files, your build might fail because CMake doesn’t see them yet. Always reconfigure:
rm -rf build
cmake -B build
cmake --build build
4. Check the basics
- Is the example building on its own, before your changes?
- Are all submodules pulled?
git submodule update --init --recursive - Are the system libraries installed (SDL, FFmpeg, …)?
5. Creative coding with GDB
C++ debuggers can be overwhelming, but a handful of commands go a long way in a creative coding workflow.
Catching segmentation faults
After your app crashes in GDB, type bt (backtrace). This prints the exact chain of function calls, pointing directly to the line in draw() or update() where a bad pointer or out-of-bounds array access occurred:
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
gdb ./build/my-sketch
(gdb) run
# ... crash ...
(gdb) bt
Conditional breakpoints
Putting a normal breakpoint inside update() or draw() will freeze your app 60 times per second — not useful. Use conditional breakpoints to pause only when something specific happens:
break application.cpp:105 if frameCount > 500
break particle.cpp:42 if position.x < 0
Watchpoints for memory corruption
If a color or physics variable keeps randomly changing to garbage data, set a watchpoint. GDB will run the app at full speed and automatically pause at the exact moment that variable is modified:
watch myColor.r
6. General debugging principles
A few mental models that help when debugging C++ creative code:
The principle of confirmation
Fixing a buggy program is the process of confirming, one by one, that the things you believe to be true about the code actually are true. Every surprise during this process is a good sign — it points directly at the bug.
Binary search in code and time
If a compiler error message is completely unhelpful, comment out half your code and see if it compiles. If it does, the error is in the commented-out half. Repeat until you have isolated the offending line.
This also works at runtime: if a loop’s value goes bad after 1,000 iterations, check its value at 500, then 750, to quickly narrow down the exact moment it breaks.
Print debugging as a fallback
While GDB is powerful, sometimes running an app in a debugger slows it down so much that timing-based bugs (like physics explosions or audio glitches) never occur. In those cases, fall back on console() or println() to track variable values and confirm whether specific functions are firing:
console("position: ", position.x, ", ", position.y);
println("frameCount: ", frameCount);
7. When stuck, ask for help
If you’re still stuck, build with cmake -B build -DCMAKE_BUILD_TYPE=Debug, run your app under gdb or lldb, and get a stack trace.
Open an issue on Codeberg with your platform, the exact command you ran, and the first error or stack trace. The community is small and friendly.
And — trust the process. Modern LLM assistants are very good at decoding C++ errors; paste the message and ask.