DEV Community

kojix2
kojix2

Posted on

Notes on Building CLI and GUI tools with Crystal

This post is just me writing down some vague thoughts that are floating around in my head right now.

Sorry if you came here expecting a well-structured tutorial — but you know, if I try to organize everything perfectly, I’ll never publish anything.


Crystal originated from the Ruby community, so there are many people who want to build web applications with it.

However, the Crystal programming language itself can be described as “a statically compiled language with a Ruby-like syntax and a garbage collector, somewhat like C with GC and type inference.”

It’s not necessarily optimized for web applications.

Personally, I wanted to use Crystal for command-line tools and GUI apps.

For some reason, though, there don’t seem to be many people building CLI tools in Crystal.

The ecosystem for building and distributing binaries wasn’t very well developed for a long time.

That used to be a real pain, but after gradually solving those issues, I think we’re now at the point where most CLI tools I want can be built and distributed in Crystal without much trouble.

On the GUI side, the situation is similar — there aren’t many libraries available.

But this isn’t unique to Crystal. GUI programming, in general, depends heavily on opaque, platform-specific APIs, which don’t always play nicely with open-source development.

Still, I decided to work on it. I created libui-ng bindings for both Ruby and Crystal.

As it turned out, libui-ng doesn’t work very well with garbage-collected languages, but I managed to make it usable anyway.

Then I got curious about Tauri and Electron — the now-famous WebView-based app frameworks.

Personally, I can barely read JavaScript, so I had no real interest in those at first, but their popularity made me curious.

Crystal also has a WebView binding.

And as I mentioned earlier, web app development in the Crystal ecosystem is quite active.

So I decided to give it a try.

I learned that “WebView” isn’t a single library — each OS (Windows, Linux, macOS) provides its own.

Projects like webview/webview and Tauri’s wry act as unifying layers over these platform-specific APIs.

Tauri itself uses WebView under the hood while also providing a framework to handle security and integration with Rust backends.

Maybe it’s possible to use TypeScript and other frontend tools with Crystal too, but personally, I prefer the more old-fashioned approach — something like Kemal + ECR, the “classic amateur” way.

When I actually started building an app with Crystal + WebView, I discovered a few things.

First, you need to pay attention to event loops and thread management.

The WebView itself runs in a separate process, and at the same time you need to run a Kemal server.

That means you often have to make it multithreaded and carefully manage your execution contexts or Fibers — otherwise, things simply won’t run correctly.

Then there’s the build, linking, and packaging pain.

I sent a few pull requests to the Crystal WebView project, which helped a bit, but building on MinGW is still rough.

MSVC technically works, but it’s just too tedious to deal with, so I decided to stay away from it.

Bundling shared libraries is also tricky.

I’d prefer to lean toward static linking whenever possible, but depending on licensing and security update concerns, it’s sometimes better to link against system or bundled shared libraries.

Even if you get the build and linking sorted out, packaging is still painful — creating application packages, Apple disk images (DMG), or Windows installers with Inno Setup, or even .deb packages.

I discovered tools like fpm, which are really useful, but in the end, I still end up asking AI to help me write custom GitHub Actions YAML and shell scripts.

And then, once you finally have a working binary, Windows or macOS antivirus software will happily flag it as suspicious.

Maybe for people doing this professionally, all this doesn’t sound like a big deal, but as someone doing it for fun, it’s a lot of work.
Even so, after all the pain, I’ve started to feel like — maybe, just maybe — this setup is actually pretty cool.


This post was translated from the original Japanese version using ChatGPT.

You can read the original post here [JA]

Top comments (0)