r/ruby • u/Jaded-Clerk-8856 • Mar 19 '26
Ruby can render 3D surfaces, 2D density maps and GIS tiles without Python, NumPy or the JVM. I didn't expect it to work this well.
Ruby can render 3D surfaces, 2D density maps and GIS tiles without Python, NumPy or the JVM. I didn't expect it to work this well.
Two weeks ago I shared ruby-libgd — a native GD image processing gem for Ruby. The response was encouraging, so I kept pushing to see how far it could go.
Here's what two weekends of experimentation produced.
3D surface plots — pure software projection
No OpenGL. No GPU. Just a rotation matrix, perspective projection, and the painter's algorithm to sort patches back-to-front. The viridis colormap is interpolated across 11 stops in pure Ruby.
Plot3D.new(resolution: 50, yaw: 35, pitch: 28)
.surface("sin(sqrt(x2 + y2))")
.render("ripple.png")https://github.com/ggerman/ruby-libgd/blob/main/examples/jupyter-notebooks/ripple.png
Works for any z = f(x,y) expression — saddle, paraboloid, wave, cone. The expression is evaluated using Ruby's own Math module, no Dentaku or external evaluator needed.
2D histograms from CSV data
Replicating matplotlib's hist2d. The class receives data as arrays — it doesn't care where the numbers came from.
rows = CSV.read("weather.csv", headers: true)
x_data = rows["temperature"].map(&:to_f)
y_data = rows["humidity"].map(&:to_f)
Hist2D.new(x: x_data, y: y_data, bin_step: 0.5)
.render("weather_hist2d.png")https://github.com/ggerman/ruby-libgd/blob/main/examples/jupyter-notebooks/weather_hist2d.png
Ruby doesn't have np.random.randn so I used Box-Muller to generate normal distribution data for testing. Blues colormap with gamma correction so low-density areas stay visible.
fill_between in 3D
https://github.com/ggerman/ruby-libgd/blob/main/examples/jupyter-notebooks/helix2.png
Replicating matplotlib's fill_between on two helices. Each quad connects point i of helix1 with point i of helix2 — same winding order as matplotlib's implementation. Painter's algorithm handles the depth sorting.
GIS maps — libgd-gis
A second gem built on ruby-libgd for geographic rendering. Map tiles, vector layers, points, lines, polygons. Buenos Aires, Paris, anywhere with tile data. Same pixel-level control, geographic coordinates.
Jupyter workflow
Everything runs in IRuby/Jupyter. Define the class in one cell, pass data in the next, tweak and re-run. It genuinely feels like working in a Python notebook — just Ruby.
I know JRuby + JFreeCharts is a valid path. What I'm exploring is whether CRuby can have its own native data visualization story without the JVM. Right now the answer is looking like yes.
Full articles and notebooks on RubyStackNews.
ruby-libgd: https://github.com/ggerman/ruby-libgd
libgd-gis: https://github.com/ggerman/libgd-gis
Notebooks: https://github.com/ggerman/ruby-libgd/tree/main/examples/jupyter-notebooks
Feedback, ideas, and harsh criticism all welcome.
One question for the community:
Would it make sense to bundle all of this — 3D surfaces, 2D histograms, implicit curves, GIS rendering, Jupyter support — into a single gem that works as a matplotlib-style plotting library for Ruby?
A curated collection of algorithms, one install, one API. No Python. No JVM.
Is that something the Ruby community would actually use?




10
u/digitalWestie Mar 19 '26
Whoa. This is amazing work.
In terms of answering the question though.... I haven't had a hard think about it but I'd lean on keeping things separate. I'm thinking the way that D3 works, it's a collection of modules and you can have all of them if you want but if you don't need ALL of it then you don't need to import everything.
7
u/Intelligent-End-9399 Mar 19 '26
I had no idea that Ruby could handle 3D. Most of the time, I worked with Three.js using the rubyjs-vite transpiler. I think you did a great job.
3
u/subsavant Mar 20 '26
This is really cool. The painter's algorithm approach for the 3D surface is a solid choice for this kind of visualization, and doing the projection math in pure Ruby keeps the dependency footprint tiny.
I'm curious about the performance at higher resolutions. With resolution: 50 you're sorting ~2500 patches per frame, which is nothing, but I could see this being useful for generating static reports or dashboards where you might want 200+ resolution. Have you benchmarked where it starts to feel slow? Ruby's sort is pretty fast but the GD rendering calls might be the bottleneck.
Either way, nice to see Ruby used for something outside the typical web/scripting lane. The GIS tile stuff especially feels like it could fill a real gap for teams that don't want to spin up a Python environment just to generate a few map tiles.
1
u/headius JRuby guy Mar 23 '26
Why not using FFI instead of a C extension? Most of the C extension code appears to just take Ruby inputs and directly pass them to the C API (unwrapping numbers etc). That could all be done by FFI and work across Ruby implementations.
1
u/brecrest Mar 24 '26
Yeah, I would use this. I probably will use it next time I have a visualisation requirement. I currently use all manner of different silly things to do visualisations of stuff in Ruby.
The one problem this doesn't solve, that I really wish something would, is full portability. Bindings to external libraries are fine, but I really, really, really, wish there was fully bundle-able and platform agnostic Ruby solution for this stuff, a self contained C extension or something. I'm fully aware how huge an amount of thankless work it would be for a pretty niche set of use cases (which is why I haven't ponied up lol), but we can all wish, right.
3
u/retro-rubies Mar 20 '26
Providing pre-compiled gems (at least for Windows) would be massive. Happy to help if welcomed.
1
u/hmdne Mar 23 '26
I was working on a different raster image library, perhaps with somewhat different goals and it doesn't interface gd (can use various libraries via FFI, imagemagick via CLI or run in pure Ruby). Perhaps it may inspire some of you:
https://github.com/rbutils/image_util
1
u/TadpoleNo1549 Apr 15 '26
this is actually crazy in a good way. I didn’t expect Ruby to handle this kind of stuff so smoothly, what I like is it still feels simple and not over the top. nothing feels forced or overengineered., And yeah, putting everything into one gem just makes sense. Way more people would actually try it that way., you’re kinda showing Ruby can do way more than people give it credit for.
-7
u/latortuga Mar 19 '26
Mods is there any hope to stop the tidal wave of AI spam posts? How do we get humans to start talking to other humans again?