Inspiration
Learning to program means building a model of things you can’t see: what’s on the call stack, where a pointer actually points, why a list changed when you only touched a different variable. Most tools either show you the final output or ask you to trust a simplified animation. I wanted a tool that shows you what your program really did, step by step, in a way you can rewind, for the languages students actually learn: Java, Python, C, and C++. And since so many people now learn with AI at their side, we wanted an assistant that works from the same real evidence instead of guessing.
What it does
See your program run: arrays, linked lists, trees, graphs, matrices, and dynamic-programming tables take shape in 3D as your code builds them, synchronized with the highlighted source line, the call stack, and the program’s output. Memory you can see (C and C++): a Memory view shows stack frames, heap blocks, and an arrow from every pointer to what it points at. It shows malloc, calloc, realloc (including when it moves a block), and free, dangling pointers, and leaks at exit. For C++, it adds object lifetime (copy and move constructors, destructors in RAII order), unique_ptr/shared_ptr/weak_ptr with real owner counts, and std::vector size and capacity. Catch memory bugs: every C/C++ run is checked by AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer. Use-after-free, buffer overflows, double frees, and leaks are reported on the exact line with a plain-language explanation. An AI assistant that checks its own work: NVIDIA Nemotron can complete TODOs, explain code and algorithms, find bugs, refactor, and generate tests. It works in the language of the file you’re on, gets real run evidence (errors, exceptions, sanitizer findings), and every suggestion is compiled in the sandbox before you can apply it. You review a diff, and Undo is always one click away. Expected output testing: run the open file with an input, compare it to the output you expect, and ask the AI to explain any difference.
How we built it
Frontend: Next.js 16, React 19, three.js for the 3D scenes, and CodeMirror for the editor. Runner: a Java HTTP service that compiles, runs, and records programs. Every job runs in a fresh Docker container with no network, a read-only filesystem, no capabilities, a non-root user, and limits on memory, CPU, processes, and file size. A real recorder per language, so nothing is simulated from source text: Java: recorded through the JVM’s own debugger interface (JDWP). Python: recorded with sys.settrace, with values shown in Python notation, including NumPy arrays. C and C++: I wrote my own small debugger. It drives the compiled program with ptrace and reads the compiler’s DWARF debug information with a parser we wrote ourselves, to find every variable and decode it by its real type. AI: NVIDIA Nemotron behind a server-only API route, with a language profile per language describing the real toolchain (C17 with gcc 13, C++20 with g++ 13, Python 3.12 with its installed packages, Java 21).
Challenges we ran into
No debugger to build on. Our environment had no gdb and no DWARF library, so we wrote both a ptrace-based debugger and a DWARF parser from scratch. Then C++ made it harder: constructors hide their names behind chains of compiler references, scopes that run destructors get split into multiple address ranges, and make_shared puts the object and its control block in one allocation. Sandboxes behave differently. Inside the sandbox’s process namespace, a C program ran as process 1, which Linux shields from signals, so a failed assert() couldn’t even stop it. /proc also numbered processes differently from the sandbox, which broke our first memory reads. And when a sanitizer stopped a program, its buffered output was lost, exactly when you most need to see it. Each one needed a real fix, not a workaround.
Accomplishments that we're proud of
Everything you see is real. Values, addresses, owner counts, and memory errors come from the running program or the sanitizers, never guessed from source text. When something can’t be observed, the app says so. A working memory visualizer for C and C++, built on a debugger and DWARF parser we wrote ourselves. An AI assistant that won’t hand you code that doesn’t compile, in four languages, grounded in real run evidence. Extensive automated tests against real recordings: runner suites for every language, sandbox security checks, browser tests of the actual workspace, and a catalog audit confirming every example’s visualization matches what it really draws.
What we learned
Debug information is a whole hidden world. Building a debugger taught us more about how compilers, linkers, and the operating system cooperate than any course had. Sandboxing is full of edge cases. Process IDs, signals, and file ownership behave differently inside isolation, and every one of those differences showed up as a real bug. With AI, verification matters as much as generation. A suggestion is only useful once it has been checked against the real compiler and the real program. Honest tools build trust. Labeling what’s measured, what’s predicted, and what can’t be seen made the app more useful, not less.
What's next for CodeVIZ
Live input: type into a running program instead of providing all input up front. Deeper container views: show the elements of std::map, set, list, and unordered containers, not just their sizes. Multi-threaded programs: record and visualize more than the main thread. A fully automatic debugging loop: let the assistant apply an approved fix, rerun the tests, and reassess on its own within a small attempt limit. Classroom features: shareable recordings, guided exercises, and graded problems in every language, not just Java. Public deployment: authentication, monitoring, and hardened hosting so anyone can use it.
Log in or sign up for Devpost to join the conversation.