Hello Rubyists!

I’m thrilled to finally announce the release of Ruby 2D v1.0. It’s a ground-up rewrite of the gem — new renderer, new DSL, new docs, new everything — and it’s been more than a decade in the making. It also very nearly didn’t happen at all. The story of how it did says a lot about where software development is right now, so before getting to what’s in the release, I wanted to share some of the journey.

The dream

I’ve always been drawn to computer graphics. As a kid, I didn’t just play games. I’d hunt for visual quirks in Mario 64 and tinker with the demos that shipped with GPUs. It carried into my early twenties: hacking around in the dev modes of Doom 3 and Half-Life 2, finding videos of graphics programmers doing their thing and watching them over and over. I became a professional programmer, but not a graphics one. It stayed a passion, even if just a hobby.

Fast forward to about 2011: I was working in Ruby, shipping web apps, and enjoying the craft. I’m not sure what the specific spark was, but I remember thinking about how elegant Ruby was, and what it would be like to bring that to the often terse and complex world of graphics programming. What if you could write Square.new and a little square just magically appeared in a window? What if square.x = 100 moved it, and square.color = 'red' changed its color? I started designing a whole little DSL where even the most complex stuff could be made simple and joyful:

on key: :space do
  player.jump
end

update do |dt|
  clouds.move dt
end

show

I thought about myself as a kid, and how something like this could have had me making games and graphics experiments early on, instead of waiting until my late teens and early twenties to start programming seriously. I was in NYC in 2012, with the “learn to code” movement in full swing. I gave Ruby lessons at General Assembly and The Flatiron School, and I kept thinking about how a “Ruby 2D” could change the way people learn.

Sharing it with the world

It was a passion project, and I casually hacked on it for a few years on the side. By 2017, it was mature enough that I thought I’d formally share it, and check off another goal of mine by speaking at RubyConf. (You can watch the talk and see the slides.) It was a great experience, the feedback was wonderful, and it renewed my belief that Ruby is the best language and community out there. I kept hacking on Ruby 2D, and met and collaborated with amazing people all over the world.

Letting go

Then 2020 hit, and priorities changed. We had recently moved, bought (and restored) a century-old house, and were about to endure a pandemic. I was also, at that point in my career, severely burned out. I’d been doing professional software development and consulting for well over a decade, mostly on high-demand civic tech and healthcare projects. In lockdown, I couldn’t imagine one more reason to stay inside in front of a screen. So I spent as much time outside as possible. I walked, hiked, canoed, boated, spent time with family, and learned to fly powered paragliders. It was awesome, and a much-needed diversion.

As for programming side projects, I honestly thought I’d never return to them. I’m still passionate about learning, tinkering, and exploring, but these “little” projects end up being all-consuming, especially once you open source them. You become tech support and a project manager for something that was supposed to be fun and free, and it becomes anything but. So I was content to let them go, let the issues pile up, and just live my life.

Then, AI happened.

From skeptic to believer

I wasn’t an early adopter. I’ve seen plenty of fads come and go, and I figured that even if some real breakthrough had been made in deep learning, it would quickly hit a plateau like the ones before it. But by 2023, ChatGPT was everywhere. I remember being out at dinner one night when a man at the next table pulled out a full-sized laptop, plopped it down between the plates, and started giving his companion a live ChatGPT demo, insisting he see how incredible this was. It was a bizarre scene, but hard to ignore: ChatGPT had made it to the dinner table. I figured I’d better find out if any of it was real.

So I hooked up Copilot in Zed and VS Code and started generating snippets, pushing it on C and Ruby problems I thought were pretty difficult (some I hadn’t been able to solve myself… yet). Like many, I kept experimenting, trying new models, having fun seeing what was actually possible (nothing serious, I concluded). The first real breakthrough for me, probably in 2024, was research. In cross-platform graphics programming, information is scattered and incomplete. There’s rarely a perfect blog post or Stack Overflow answer covering the exact thing you’re trying to implement or fix. But I could hand an LLM a garbled mess of intent as a simple text prompt (no agents, no reading code) and get back a tailored, specific answer I could actually use. Amazing!

Putting agents to work

Then 2025 hit — the year of the agents — and everything changed. I was still primarily in Zed, using their LLM integrations. Because I had a couple of “popular” repos, GitHub had given me a free Copilot subscription, which at the time included unlimited access to GPT-4.1. It was a great model, and I pushed it hard to find its limits, abusing context windows, tool calls, everything. That’s when I figured I might as well give it a real codebase to grind on.

Opening up Ruby 2D after years away gave me a good case of coding horror. The professional codebases I’d worked on had teams and resources behind them; this one had a single person’s nights and weekends, and it showed. So the first task was refactoring. GPT-4.1 was excellent at taking swaths of code and rewriting for clarity and consistency, and I churned through thousands of lines of the gem, both Ruby and C. Again, incredibly useful.

But I had something harder in mind: a bug of the worst kind. An inconsistent segfault, somewhere in the native C extension that managed rendered font textures. Something was being initialized or released unpredictably, likely Ruby’s garbage collector freeing memory while a pointer to it was still in active use. This was fall 2025, and it was as “frontier” a test as I could come up with, and genuinely valuable to solve. I loaded up Zed’s agent mode with GPT-4.1, and after a long, roundabout attempt, I killed the session. Maybe it would have gotten there, but the path was too slow and circuitous. So I tried GPT-5 mini, a faster model that could really grind through tokens, exactly the kind of tracing this sort of debugging needs. A few hundred thousand tokens later, it narrowed in on the cause, and GPT-4.1 helped with a proper fix. It wasn’t the kind of problem that needed vast knowledge or some breakthrough insight, but these nasty, time-consuming, head-against-the-wall problems are exactly the kind you want to offload. I was genuinely surprised, and I knew some threshold had been crossed.

The Zed editor open on the simple2d C codebase, with src/image.c showing S2D_CreateImage initializing an image struct field by field. An agent panel on the right walks GPT-4.1 toward the bug: it proposes that img->texture and img->surface were never set to NULL when the struct was created, and suggests initializing both.
A Zed agent session from that era: quizzing GPT-4.1 about a pointer bug I had just fixed myself. “I still got ahead of you (for now, lol)” — that “for now” didn’t last long.

The rewrite

By December 2025, Claude Code and Opus reached a similar threshold. It wasn’t some dramatic release or distinct moment, just the combined model and harness making more possible. Like many around that time, I hacked over the winter break on all sorts of things to get a feel for this new agent-enabled world we were heading into. In late January 2026, I finally gave in, gifted myself a Claude Max subscription, and fully embraced these new tools.

This spring was a blur. Claude Code was autonomously finding and fixing anything I could throw at it. Codex also made its (re)debut with a decent harness, but I mostly stuck with Claude (its Ruby style was much better, closer to what I’d call idiomatic). As the models and harnesses got better, so did my ambitions. I was having fun building Ruby 2D demos and games, but I knew it was time to rewrite the gem itself, and that my agents could handle it. We talked through goals, architecture, style, intent, all of it, and over hundreds of sessions, the gem was rebuilt from the ground up. Everything I dreamed about in that 2017 RubyConf talk, I was finally able to build. 😍

What’s in v1.0

A whole lot! You can read the commit message if you like, but here are the highlights.

The entire foundation is new. Ruby 2D now renders through SDL3 and its GPU renderer, which means Metal, Direct3D, or Vulkan depending on your platform. The old OpenGL pipeline, some 1,100 lines of hand-rolled shader and context management, is simply gone. Everything is faster, and everything can rotate now, too.

The DSL grew up in all the ways I dreamed. There are new shapes (Ellipse, Polygon, Polyline), fills and strokes, dashed and gradient lines, a far more capable Canvas, sprite sheets, tilesets, a built-in bitmap font, and a new Audio class. Gamepads are first-class, with multiple controllers, rumble, even LED colors. And a fun new pattern: any object on screen can respond to input directly.

square = Square.new(color: 'red')

square.on :hover do
  square.color = 'random'
end

square.on :click do
  square.rotate += 45
end

Your apps can now run in the browser, too. The same Ruby code builds to WebAssembly, and you can play with it right now at ruby2d.com/try.

And quality got the same attention as features: 866 specs, 35 examples (from snake to ray casting), a benchmark harness, and comprehensive documentation for humans and agents alike. There’s even a ruby2d agents command that coding agents can call to teach themselves how the gem works. (It felt only right that a library rebuilt with agents should be a great one to build with them, too.)

I think it impressively clears the quality software bar. Ruby 2D finally feels like a professional piece of software.

Living in the future

It’s hard to overstate how substantial this all feels. For those of us with years of hard-fought experience and domain knowledge, these agents are a radical unlock. Everything I know how to evaluate, but never had the time or endurance to build, is suddenly within reach. And beyond the code, having an on-demand thought partner to work through any idea or challenge, at any hour and at whatever depth, is incredibly profound. The reason I walked away from side projects was that they demanded more than one person could sustainably give. That constraint is just… gone.

Now, I know it’s not all rainbows and sunshine out there. We’re entering an age of great uncertainty, and I feel the anxiety too. The disruption to our industry, and well beyond it, will be real, and I won’t pretend to know how it plays out. But when I look at what I’ve been able to achieve, just myself and some smart agents, I feel like a bionic person, living in the future. At this point, I believe I can build anything I can imagine, and that’s an exhilarating feeling.

Fifteen years ago, I daydreamed about typing Square.new and watching a little square magically appear in a window. Somewhere out there is a kid like I was, poking at games and wondering how the graphics work. They won’t have to wait until their twenties to start building. They can open a window today, put a square in it, and ask their computer to help make it dance.

Give it a try: gem install ruby2d, and visit ruby2d.com to learn and get inspiration. I’d love to see what you build!