Show Me How, an algorithm visualizer in zig

Show Me How:


Hi, Show Me How is a project I’ve been wanting for a long time. When I was tutoring people on how to program, I really wished I could make some of the concepts more visual. Earlier this year I was working on a graph optimization algorithm and again wished I could show it rather than just explain the intuition behind it.

The best visualizations available right now would probably come from the python manim library, but creating visualizations this way still might not be incredibly easy, and could require re-implementing the algorithm within manim. This might be difficult if the code you want to visualize is written in another language. Plus, I’ve seen visualizations made with manim and use of the library doesn’t make the visualization good!

Show Me How works by reading a command script from stdin so I don’t have to reimplement my algorithms, just modify them to produce an extra script which can then be run by this program. Layout and animations are handled automatically. Scripts can be played live, as the source program generates them (for long-running tasks) or from a script file, and all operations can be played backwards and forwards.

Right now, I’m trying to solidify the architecture and patch up some of its weaknesses, then move onto graphs, using graphviz as the layout engine. After I have that done,

Project link: https://codeberg.org/splinterofchaos/Show-Me-How

Supported Zig versions

I’ve been developing on zig-0-16.

AI / LLM usage disclosure

A disclosure feels more like a confessional to me because a few months back on another project, I got so addicted to prompting that I depleted all my dopamine. Since then, I’ve been mostly writing all my own code, but sometimes I feel weak, tired, lazy, and ask claude to do something here or there. I consider this a personal failing, and now a public embarrassment.

Disclosing also feels like a confessional because I feel it destroys any credibility I or my project might have, but not disclosing would just be dishonest. But I hope the shame I feel from writing this disclosure helps me kick the habit for good.

17 Likes

Going to share a link to it?

OMG, I forgot to do the ONE thing this post was supposed to do :stuck_out_tongue:

Sorry, it’s here: https://codeberg.org/splinterofchaos/Show-Me-How

I’ll update the first post as well.

1 Like

Have you tried it on Windows (10)? When I run this:

Get-Content ./example/bubble.zig -Raw | zig build run

(the powershell equivalent of cat .. | ..), I get this

reader: EOF
UnrecognizedOption: 1:1: unrecognized instruction: const std
error: UnrecognizedOption
D:\experiment\zig\Show-Me-How\src\strings.zig:54:9: 0x7ff602e3c932 in fail__anon_60927 (showmehow_zcu.obj)
        return e;
        ^
D:\experiment\zig\Show-Me-How\src\Interpreter.zig:476:12: 0x7ff602e3ae90 in interpretOne (showmehow_zcu.obj)
    } else return self.parser.fail(
           ^
D:\experiment\zig\Show-Me-How\src\Interpreter.zig:307:13: 0x7ff602e33254 in interpret (showmehow_zcu.obj)
            try self.interpretOne()
            ^
D:\experiment\zig\Show-Me-How\src\Interpreter.zig:111:24: 0x7ff602e32eaa in executeNext (showmehow_zcu.obj)
            break :lst try self.interpret();
                       ^
D:\experiment\zig\Show-Me-How\src\main.zig:189:17: 0x7ff602d0b01c in run (showmehow_zcu.obj)
            if (try interpreter.executeNext(
                ^
D:\experiment\zig\Show-Me-How\src\main.zig:67:9: 0x7ff602d0c8d6 in main (showmehow_zcu.obj)
        return err;

Sorry, I should have improved the README a bit more for usage instructions before posting.

example/bubble.zig is a sample program that produces a show me how script. The bubble and mergesort examples both have targets in zig build so you can

$ zig build bubble | zig build run

I just tested on windows and did notice that the bubble script prints ? at the end of each multi-line text line, which it doesn’t on Linux (WSL). Should be a simple fix though.

But if you already had a script, you would use Get-Content/cat.

EDIT: Hopefully the readme is a bit more clear now. Thanks.

EDIT2: The ? rendering is fixed, but with a caveat that I just delete any \r I see. I think that’s not unicode friendly, but I don’t support unicode at this time.

1 Like

Thank you! that worked. I’ll look at the code now.

very sengoku-jidai coded LLM disclosure

Maybe, but I felt like if I just disclosed “there’s LLM code in here,” that would’ve been dishonest. I think one should be proud of the code they upload, but the parts that are AI generated are significantly below my standards. Owning up to the negligence of allowing bad code in my repo feels a little better than claiming this is the best I can do.

The paintfil example, for example, isn’t even the correct algorithm and I’ve been meaning to rewrite it myself, but it works well enough so I haven’t bothered to touch it.

no i respect it, it’s just funny

1 Like

Wow! Spoken like a true addict. I’m impressed.

Best of luck on your journey, and enjoy your work.

Don’t feel too bad. There is a lot of negativity towards AI in the Ziggit community, but remember that is just a subset of the programming community as a whole.

AI is a tool, much like any other. It’s how you use it that matters.

For me, I don’t get much joy from building something from the from the ground up. I’d much rather polish a turd into an emerald than craft a diamond from scratch.

I don’t care if people use AI or not, I care if the code is good, whether the project is cool (or practical) and whether or not the creator is motivated to continue.

Sometimes taking shortcuts for the sake of your sanity is the right call, and that has existed long before AI, as anyone who’s ever programmed at 5pm on a Friday, or had to sus out an outage during off hours will know.

I would say focus on the interfaces/architecture and the UI/customer facing code. Things like algorithms can be easy to swap out later, and if you profile the code, you may find that it’s not even worth optimizing in the first place because something unexpected is actually eating up all your runtime.

Anyways, I apologize if I’m being a bad influence on you, but with all the self deprecation, I just had to speak up.

FWIW, it looks like you’ve built something cool, and I think that’s something to be proud of :slight_smile:

2 Likes

Thanks, @sbrow , I get what you’re trying to say, but I’m in that subset who’s negative towards AI. I invested in it partly because as a job seeker, I felt not understanding AI would hurt my chances, but zig’s anti-AI stance was part of what attracted me here.

What I view as truly self deprecating was when I told myself that I wanted a bucket fill demo (although I now know the name I was looking for was flood fill/scanline fill), but I knew that procedurally generating walls would require perlin noise and I said “I don’t know how to do that and I’m no Sebastian Lague. I’ll ask AI to do it.”

Yesterday, I felt motivated. Within half an hour or so of research had landed on the reference implementation written by Perlin himself. It’s short and well organized, with comments that are surprisingly clear about what the code is doing even if some of the math is unexplained. Still, I found it approachable enough and there are plenty of explanations online.

It did take some fiddling to set up, but it was surprisingly easy, and easy to manipulate. I then implemented the algorithm that I intended, and this involved a little trial and error and I’m still experimenting with zig’s syntax a bit, but it works!

I find this algorithm particularly fun to watch because at first it’ll look like it’s systematically filling out an area and gets randomly distracted and runs off in an entirely different direction. I used a slightly optimized version of the algorithm in order to keep the stack (represented by red node) small. Otherwise, the algorithm spends half its time adding every node on the graph to the stack and it doesn’t look as good, also much slower.

The noise function was interesting to do in zig because I was able to use \@Vector and reduce some statements down, although whether that’s better or worse who can say.

https://codeberg.org/splinterofchaos/Show-Me-How/src/branch/main/lib/Perlin.zig

https://codeberg.org/splinterofchaos/Show-Me-How/src/branch/main/example/floodfill.zig

The thing that gets me the most about all this AI hype is that sometimes I start doubting my own abilities. People say “coding is solved,” but it was solved 1992 with the release of vim. I think the mythologizing of AI (they literally called it “mythos”) goes hand-in-hand with a portrayal of programming as something inherently difficult and beyond human capability. Turns out the kid shows were right. Just believe in yourself.

EDIT: phrasing

7 Likes

I was curious about the permutation table in the original implementation. According to wikipedia, the table isn’t special, it’s 0-255 is a random order. But what if it was IN order?

SMH is definitely not a good perlin noise visualizer, but shadertoy is so I wrote it out.

https://www.shadertoy.com/view/NfyGDV

It was a bit hard to look at so I colored the ranges differently using mix and smoothstep. I think it actually looks kinda neat so I thought I’d share in case anyone was interested.

2 Likes

Anyone who says that coding is “solved” is at best misinformed, and at worst dillusuonal.

Connect 4 has been ‘solved’. Checkers has been ‘solved’. Chess (last I checked) has not.

Something I think was pointed out to me in a Casey Muratori video is that unlike many other technical disciplines, programming is less than 100 years old. We simply haven’t had the time it takes to accumulate wisdom of what is and isn’t good programing. Sure, we can compare the assembly or machine code between 2 functions and determine which will run faster, but things quickly get more abstract when you start zooming out.

I think many people here would agree that imperative programming is generally better than Object Oriented Programming or Functional Programming, but that’s not even close to univerally accepted, and in the case of FP, there are certain domains where it does shine.

An interesting read is Scarf has reluctantly moved away from Haskell, in which the author, a member of the Haskell Foundation board started transitioning his company from using Haskell to Python.

That’s about the biggest jump in languages I can concieive of. In short, the build times became untenable (15mins+), and they decided that instead of using Haskell’s type system to enforce boundaries, they could use AI to enforce them. Now that sounds pretty crazy to me, but allegedly it’s working for them.

My point being that in a ‘solved’ system, a jump like that would be inconceivable.

AI is not special here, it’s just one shift in the landscape. In my domain (web dev), I personally had a similar shift when I moved away from state based fronted tools (React, Svelte, etc.) towards REST based ones (htmx, DataStar), and at this point, it completely blows my mind that anyone can look at a React or Next.js app and think it’s the right way to do things. And yet, the React community keeps going strong.

…anyway, sorry for going off the rails, and I swear I’m not an AI shill :sweat_smile:

1 Like

I feel I’m at risk of derailing my own thread, but oh well, this is fun. YOLO

In short, the build times became untenable (15mins+), and they decided that instead of using Haskell’s type system to enforce boundaries, they could use AI to enforce them. Now that sounds pretty crazy to me, but allegedly it’s working for them.

Read the article. I can’t directly contradict that, but I am suspicious. They talk about being able to fix bugs fast thanks to AI and how “Resisting this kind of productivity is not an option anymore,” but in what way does increased productivity make the business more sound? Does fixing bugs cause more people to purchase your software? Are there to-be-implemented features that would unlock new revenue streams? And if so, how much time does that stream need to flow in order to earn the cost of development back?

I do not see a correlation between how productive a tech company is and how much money it makes. Just a few years ago, when common sense was still common, I thought everyone agreed that quality was king. Now we’re making quality/productivity trade-offs and I don’t see why.

They talk in that article about going from high performance Haskell to Python and I can’t see how that isn’t making their business less efficient. Their servers are probably fast enough, but now they’ve got all the overhead of running their code through an interpreter, burning more CPU with every request. They still have to pay their employees, they now also have to pay Anthropic, and they also need to purchase more cloud CPU. I also doubt their employees are as “energized” as they claim. Just as a general rule, if you employ other people you can’t expect them to be honest about how they feel (don’t bite the hand that feeds) so you’re just not able to speak on that.

I think productivity is a mind virus. People are acting like there’s a pot of gold at the end of the rainbow, we just gotta get the work done and it’s ours. That’s when life truly begins. But one day they’ll realize that the grind was their life. That the challenges they faced were not obstacles blocking the way, just more stops on their path through life.

I have to say, though, I adore Haskell. I think anyone who tries to build non-trivial software in it is nuts, but it really changed the way I viewed programming.

4 Likes

Glad we can have a lively debate, even if it is off-topic :smiley:

I think an important quote from that article is actually the line right above it:

From customer call → ticket filed → PR opened → PR reviewed and iterated → merged → deployed, we can sometimes have bug fixes live before I get off the call with a customer. Resisting this kind of productivity is not an option anymore.

If we extrapolate, assuming the previous workflow was something like:

local fix (15min build) → CI Check (15min build) → Push to prod (15min build, but maybe cached from step 2)

then that’s like a 1hr minimum between ticket opened and problem solved, that’s potentially a big difference. And if the difference in productivity is entirely waiting for build times, I think it’s hard to build a case that sitting around waiting is preferable. At least in my experience, if you can solve people’s problems immediately, customers (or even co-workers) can be amazed. And impressed customers stick around, and more importantly tell their friends about the quality of service you provide.

Just a few years ago, when common sense was still common, I thought everyone agreed that quality was king.

I really wish I could work in that world, got any job openings? :laughing:

I work in the world of (non-high performance) web, and unfortunately quality has never really been the name of the game[1]. Although painful to admit, unless your customers are programmers, they almost never care about whether your code is beautiful or spaghetti - they care about whether your app works, is smooth, and whether or not it actually serves them.

from Zig Zen

Together we serve the users.

From that perspective, I’d say stability and how quickly you can respond to customer demand (without interrupting existing features) are genuinely important metrics.

They talk in that article about going from high performance Haskell to Python and I can’t see how that isn’t making their business less efficient.

My take on it is that the build times were the main pain point for them, so they wanted to switch to an interpreted language. I also suspect the reason they chose python over ruby or JS is because it has the best integration with Nix, which they are clearly using.

Anyway, thanks for indulging my sidebar :smiley:

I also enjoy Haskell, though I’m not smart enough to fully utilize it.


  1. Until it is. If you’re a FAANG company and you need to superscale, things change, but for most apps with single digit active concurrent user counts, this never comes into play. ↩︎

2 Likes

I wrote out a response, but then I was working on my code earlier today, making some of the error types more explicit, and I had this thought “wow, listing out errors gives me a better understanding of what functions do and what they touch!” And I realized allowing discussions like this would make it harder to have discussions like that. But I’d like to offer that if you’d like to hop on discord at some point for a voice chat, maybe we could have an interesting conversation.

I don’t believe in debate, but I do like to test my thoughts and opinions on other people and learn new perspectives.

1 Like

debate’s just a fancy word for talking

1 Like

Cool! Partially what I am moving toward. Do you have only dots? What kind of scope and “tools” do you plan? I mean only matrixes and what graphs mean in your case?

1 Like

Thanks for the questions. Sorry, this is going to be a bit of a wall of text. I have a lot of ideas, but I’m not great at planning or structuring them.

At least right now. I needed a fundamental graphical unit to base operations like swap and copy, and I think this could work for more complex data types. For example, if you have <x,y> vectors you need to represent, perhaps there could be a structure where a bounding box is drawn behind nodes to group them.

The biggest issue right now is that dots are very limited in what they can display (currently limited by char count) and so I’ll probably move on to drawing ellipses that can store any amount of data, like graphviz does.

What kind of scope and “tools” do you plan?

It’s all very loose right now. After I have graph support, I’ll be able to visualize the algorithm that inspired me to work on this. All it needs is to have the nodes and arcs correctly rendered and be able to color them.

It feels like graphs could probably take care of most usecases. To visualize Show Me How itself (and this is a goal), I’ll need graphs to represent control flow, and a way to render arcs along a flat array for the label tree.

Trees will likely require some dedicated code so that they’d render how you expect, I don’t expect I can use graphviz for that. A friend of mine suggested they wanted to see an AVL tree, which would require special support, but given how often trees appear in computer science, seems worth it.

Special data structures like associative containers might not be difficult to visualize on their own, but when they’re being used as storage for a larger algorithm might need some special rendering like this:

The goal is to support the widest set of use cases with the smallest set of primitives with simplest architecture. To that affect, map/dictionaries, hash maps, sets, and other structures will likely be just once concept in SMH. There will always be more exotic data structures and I think the best I can really do there is just try to make SMH easy to extend.

Right now, SMH allows you to step through algorithms, play, or rewind, but I plan to add support for editing a script while executing it so you could in theory do things like create a popup to explain what a value is doing, then save the script again.

I mean only matrixes and what graphs mean in your case?

By graphs, I just mean node diagrams with arrows between nodes. Though internally in SMH, I consider a “graph” to be any structure where they layout is determined by you or an external layout engine like dot or neato.

One use case of SMH that I envision is if I added support for displaying arbitrary textures, you could have a CPU texture with cutouts for the different registers and the CPU could be a graph node with the texture as one child, centered, and then you manually place the register nodes to line up with the texture. This could be used to create a visualization of assembly code.

1 Like