Back
Before you start

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 added audio() or register_library, make sure they are in the right phase (settings() for size() + audio(); setup() for register_library).
  • CMakeLists.txt: Added a new .cpp? You must rerun cmake -B build. CMake’s GLOB evaluates at configure time, not build time, so new files remain invisible to the compiler until you reconfigure.
  • audioEvent(): No new, no malloc, no std::vector::resize, no string concatenation. Pre-allocate everything in setup().
  • Any .cpp calling ImGui::*: Make sure the call site is between imgui_library.begin_frame() and imgui_library.end_frame().
  • Any .cpp using PGraphics (FBOs): Make sure every beginDraw() has a matching endDraw(). 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. Using new, malloc, std::vector::resize, or std::string concatenation inside this callback can cause silent buffer underruns or crashes under load. The fix is always the same: pre-allocate your std::vector<float> buffers and any other resources in setup().

  • The CMake GLOB trap: When you add a new .cpp file and get an “undefined reference to vtable” error, it is because CMake’s file(GLOB ...) only scans the directory at configure time (when you run cmake -B build), not at build time (when you run cmake --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() and circle()) and flushes them at the end of draw(). Calling the free function flush() right before imgui_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 with ImGui::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.

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.