Turn Your Code Into Pixel Art

In 2022, I found out what the average programming language color was. Today, I want to transform code itself into pixel art (in less than 100 LOC).

What?

The plan is very simple: I’ll parse a Ruby file to collect all token types, assign each token a color, and then render each one as a pixel in a grid. Sounds like a plan, right?

If you’re only interested in the script, here it goes. Just run ruby code_picture.rb your-file.rb, and you’ll get your abstract pixel art. I’ll be going over some of the most interesting parts of that code. Stick around, and you might learn something cool.

Parsing Ruby

The whole reason this silly idea exists is me trying out the new Ruby parser: Prism. So, that’s what I’m going to use to parse code. It’s pretty cool that it parses Ruby itself (at least since CRuby 3.3) and also works as a gem.

Anyway, to use this bad boy in a single-file script, I’ll use an inline Gemfile. It will install any dependencies needed when we run the code. I’ll also install Nokogiri because it will be useful soon.

require "bundler/inline"

gemfile do
  source "https://rubygems.org"
  gem "prism"
  gem "nokogiri"
end

After that, using the Prism Ruby API is pretty straightforward:

result = Prism.lex_file(file)
if result.failure?
  errors = result.errors.map { "  - #{_1.message}" }
  abort("Failed to parse file:\n#{errors.join("\n")}")
end

Note that I am only lexing the file (instead of fully parsing it) since we don’t need an Abstract Syntax Tree.

Creating Pixels

If parsing is successful, our tokens will be on result.value. Now, I want to create a Pixel object out of them. First of all, let’s create a data object that holds that information.

Using Ruby 3.2’s Data class makes defining Value objects a walk in the park:

Pixel = Data.define(:color, :token_type, :token_value)

So, each Pixel has a color, the token type (e.g. KEYWORD_DEF, PARENTHESIS_LEFT), and its value (i.e., the variable name, the string contents, etc). While we’re at it, let’s create a function that returns random colors and a THEME (a mapping of token types to colors):

# nicer colors than just pure random
def rand_color
  "hsl(#{rand(0..360)},#{rand(70..110)}%,#{rand(40..90)}%)"
end

# This ensures random colors while also ensuring the same
# color is used for the same token type.
random_colors = {}
RANDOM_COLORS_THEME = ->(token_type) {
  random_colors[token_type] ||= rand_color
}

Cool! Now let’s create the pixel objects. I’ll also filter out some undesired tokens (things like EOF and new lines) in the way. Our friend filter_map is here to help with that:

# Prism returns a token and its location in an array.
# I'm using argument destructuring to ignore the second
# value in the array
pixels = result.value.filter_map do |(token, _)|
  next if token.type == :WORDS_SEP
  next if token.type == :IGNORED_NEWLINE
  next if token.type == :NEWLINE
  next if token.type == :EOF
  next if token.type == :MISSING
  next if token.type == :NOT_PROVIDED

  Pixel.new(THEME[token.type], token.type, token.value)
end

Now, we’re ready to render HTML.

Rendering HTML

For the fun of keeping it all in Ruby, I decided to use Nokogiri to generate HTML. To keep all the pixel rows about the same size, I used our old friend Math.

row_size = Math.sqrt(pixels.size).ceil

Who would’ve thought square roots were actually useful?

I also used CSS’s attr to pass data from HTML to CSS. The template looks something like this:


builder = Nokogiri::HTML4::Builder.new do |doc|
  doc.html do
    doc.style do
      # Some CSS here
    end
    doc.body do
      pixels.each_slice(row_size).each do |row|
        doc.div(class: "row") do
          row.each do |pixel|
            doc.span(
              class: "pixel",
              style: "background-color: #{pixel.color}",
              "data-content": "Type: #{pixel.type}; Value: #{pixel.value.inspect}"
            )
          end
        end
      end
    end
  end
end

Then, it writes the HTML to a file and opens it with the (MacOS-specific) opencommand:

File.write("code-picture.html", builder.to_html)
`open code-picture.html`

And… done! Here’s what the code_picture.rb script itself looks like:

A square grid of pixels with random colors

Hopefully, it’s not underwhelming! 😅

A Challenge For You!

If you like this, I have a polished version of this script released as a gem. It has several customization options so you can define your own themes, control the pixel size, the number of pixels per row, and more! Give it a try!

Now, I want to make a challenge for you. Can you write valid Ruby code that also renders as cool pixel art? Feel free to use the customization options above to achieve your goal! If you create something cool, please share it on Twitter/X or Mastodon using the hashtag #codepicture (you can also tag me @MatheusRich)! I’d love to see what you come up with!