The letter A styled as Alchemists logo. lchemists
Published April 1, 2023 Updated February 20, 2024
Tone Icon



Provides a customizable ANSI text colorizer for your terminal. This allows you to encode plain text that renders as colorized output. This is great for enhancing Command Line Interfaces (CLIs) and terminal output in general.


  • Provides default styles with multiple options for unique combinations.

  • Provides a single object for encoding and decoding colorized text.

  • Provides aliasing of color configurations.

  • Provides quick access to default and aliased styles.


A screenshot of colors


  1. Ruby.


To install with security, run:

# 💡 Skip this line if you already have the public certificate installed.
gem cert --add <(curl --compressed --location
gem install tone --trust-policy HighSecurity

To install without security, run:

gem install tone

You can also add the gem directly to your project:

bundle add tone

Once the gem is installed, you only need to require it:

require "tone"


Basic usage is as follows:

tone =
tone["Success!", :green]  # "\e[32mSuccess!\e[0m"

There is a lot more you can do with this gem so the following sections will delve into the specifics.


As you saw earlier, you can encode plain text as colorized text using #[]. Use of the #[] method is an alias to the longer #encode method. This allows you to use minimal syntax to create colorized text. Here’s a few more examples:

tone =

# With symbols.
tone["Success", :black, :on_green]    # "\e[30;42mSuccess\e[0m"

# With strings.
tone["Success", "black", "on_green"]  # "\e[30;42mSuccess\e[0m"

# With no styles.
tone["Success"]                       # "Success"

# With any object that responds to `#to_str` or `#to_s`.
tone[, :green]              # "\e[32m#<Object:0x000000010f095668>\e[0m"

# With nil.
tone[nil]                             # ""

# With interspersed nils (nils are ignored).
tone["Success", nil, :green, nil]     # "\e[32mSuccess\e[0m"

The first argument is the text you want to encode/colorize. This can be a word, phrase, paragraph, or entire document. All arguments that follow after the first argument are style arguments which allow you to style the color of your text as you see fit. In this case, the "Success" text will use a black foreground on a green background. The styles available for you to use will be explained shortly, though. For now, know that #[] is shorthand for #encode so any of the above examples could be replaced with #encode messages. Example:

tone =
tone.encode "Success", :black, :on_green  # "\e[30;42mSuccess\e[0m"

Both methods are available to use depending on your preference.


Once your text has been encoded with colors, it can be nice to decode the colorized text back to plain text along with additional metadata. This is helpful — as an example — for testing purposes since you might not always want to deal with the hard to read escape characters. If we build upon the examples from the Encode section, we can decode our colorized text into plain text with extra metadata:

tone =

tone.decode "\e[30;42mSuccess\e[0m"  # [["Success", :black, :on_green]]
tone.decode "\e[37;41mFailure\e[0m"  # [["Failure", :white, :on_red]]

Notice we get an array of sub arrays which mimic the original arguments passed to #encode. This allows you to encode and decode with minimal effort. Here’s a more complex example where a sentence is used and formatted with the Amazing Print gem:

tone =
ap tone.decode("We turned a \e[37;41mfailure\e[0m into a \e[30;42msuccess\e[0m!")

# [
#   [
#     "We turned a "
#   ],
#   [
#     "failure",
#     :white,
#     :on_red
#   ],
#   [
#     " into a "
#   ],
#   [
#     "success",
#     :black,
#     :on_green
#   ],
#   [
#     "!"
#   ]
# ]

For plain text, you get a single element array but for colorized text, it will be broken down into an array of arguments. This allows you to easily iterate over this structure for parsing, transformation, or pattern matching purposes.

Here’s another example where a paragraph is used:

tone =

paragraph = <<~CONTENT.strip
  Yesterday \e[30;42mwent well\e[0m
  but tomorrow will be \e[37;41mmore challenging\e[0m.

ap tone.decode(paragraph)

# [
#   [
#     "Yesterday "
#   ],
#   [
#     "went well",
#     :black,
#     :on_green
#   ],
#   [
#     "\nbut tomorrow will be "
#   ],
#   [
#     "more challenging",
#     :white,
#     :on_red
#   ],
#   [
#     "."
#   ]
# ]


To display defaults, use:

tone =

The above will output something similar to what you see below (minus the categorization) of key and value which will allow you to pick and choose the style or combination of styles you desire.

  • Styles

    • clear

    • bold

    • dim

    • italic

    • underline

    • inverse

    • hidden

    • strikethrough

  • Foregrounds

    • black

    • red

    • green

    • yellow

    • blue

    • purple

    • cyan

    • white

    • bright_black

    • bright_red

    • bright_green

    • bright_yellow

    • bright_blue

    • bright_purple

    • bright_cyan

    • bright_white

  • Backgrounds

    • on_black

    • on_red

    • on_green

    • on_yellow

    • on_blue

    • on_purple

    • on_cyan

    • on_white

    • on_bright_black

    • on_bright_red

    • on_bright_green

    • on_bright_yellow

    • on_bright_blue

    • on_bright_purple

    • on_bright_cyan

    • on_bright_white

These are the defaults for which you can mix-n-match as desired to produce colorful output. For example, if you want black text on a green background with an underline, you could use:

tone =
puts tone["Success!", :black, :on_green, :strikethrough]


For situations where you’d like to find a code (or codes) for a symbol you can use the following:

tone =

tone.find_code :green                # 32
tone.find_code :bogus                # nil
tone.find_codes :green               # [32]
tone.find_codes :red, :green, :blue  # [31, 32, 34]
tone.find_codes :bogus, :invalid     # [nil, nil]


Much like with the codes, mentioned above, you can find a symbol (or symbols) for a code too:

tone =

tone.find_symbol 32           # :green
tone.find_symbol 666          # nil
tone.find_symbols 32          # [:green]
tone.find_symbols 31, 32, 34  # [:red, :green, :blue]
tone.find_symbols 666, 999    # [nil, nil]


You can alias combinations of default styles with a descriptive name for shorthand reuse. This allows you to reduce duplicated effort and speed up your workflow. Here are a few examples:

tone =
tone.add_alias :success, :black, :on_green
tone.add_alias :failure, :white, :on_red

tone["Success!", :success]  # "\e[30;42mSuccess!\e[0m"
tone["Failure!", :failure]  # "\e[37;41mFailure!\e[0m"

Notice that the first argument is your alias and all arguments after the first argument is the list of styles. Once added, both the :success and :failure aliases can immediately be used. You can also add multiple aliases, at once, by chaining your messages:

tone =
           .add_alias(:success, :black, :on_green)
           .add_alias :failure, :white, :on_red

tone["Success!", :success]  # "\e[30;42mSuccess!\e[0m"
tone["Failure!", :failure]  # "\e[37;41mFailure!\e[0m"

Aliases — and associated styles — can be symbols or strings. The following, despite using strings, is identical to the above:

tone =
           .add_alias("success", "black", "on_green")
           .add_alias "failure", "white", "on_red"

tone["Success!", :success]  # "\e[30;42mSuccess!\e[0m"
tone["Failure!", :failure]  # "\e[37;41mFailure!\e[0m"

To see the list of all aliases added, use:

tone =, :black, :on_green).add_alias :failure, :white, :on_red
ap tone.aliases

# {
#   :success => [
#     :black,
#     :on_green
#   ],
#   :failure => [
#     :white,
#     :on_red
#   ]
# }

To get a specific alias, use:

tone = :success, :black, :on_green
tone.get_alias :success

# [:black, :on_green]

In the case of a default, you’ll only get back the given key: :green  # :green


There are several checks performed which might result in a Tone::Error if not properly used. Here’s a few examples of what you might see.

tone =

tone.add_alias :bogus
# Alias must have styles: :bogus. (Tone::Error)

tone.add_alias :bogus, nil
# Alias must have styles: :bogus. (Tone::Error)

tone.add_alias :red, :red
# Alias mustn't duplicate (override) default: :red. (Tone::Error)

tone.add_alias :bogus, :invalid
# Invalid style (:invalid) for key (:bogus). (Tone::Error)

tone.add_alias :success, :black, :on_green
tone.add_alias :success, :black, :on_green
# Duplicate alias detected (already exists): :success. (Tone::Error)

tone.get_alias nil
# Invalid alias or default: nil. (Tone::Error)

tone.get_alias :bogus
# Invalid alias or default: :bogus. (Tone::Error)


When using this gem in your project, you might find it convenient to use the have_color RSpec matcher. This matcher is optional and must be manually required for use in your spec helper:

# spec_helper.rb
require "tone/rspec/matchers/have_color"

Once required, you can leverage the matcher in any spec as follows:

RSpec.describe DemoPresenter do
  subject(:presenter) { color: }

  let(:color) { }

  describe "#to_s" do
    it "renders colored text" do
      expect(presenter.to_s).to have_color(color, ["Test 0.0.0: A test.", :bold])

The first argument must be an instanced of Tone because you might have custom aliases which must be known in order to validate your spec. All subsequent arguments (one to many) that follow after the first argument can be a list of decoded tuples as would normally be answered by Tone#decode.

In situations where the spec fails, you’ll get a formatted error so you can quickly fix as necessary:

expected "\e[37mtest\e[0m\n" to have color decoded as:
["text", :blue],

but actually is:
["test", :white],


The following are worth considering, when using this gem, to help keep your implementation consistent.

Order your arguments by style, foreground, and background when encoding:

# No
tone["test, :underline, :on_black, :white]
tone["test, :white, :underline, :on_black]
tone["test, :on_black, :white, :underline]

# Yes
tone["test, :underline, :white, :on_black]

Order your arguments by style, foreground, and background when adding aliases:

# No
tone.add_alias :demo, :underline, :on_black, :white
tone.add_alias :demo, :white, :underline, :on_black
tone.add_alias :demo, :on_black, :white, :underline

# Yes
tone.add_alias :demo, :underline, :white, :on_black

These are not hard requirements but these little touches will help improve readability. 🎉


To contribute, run:

git clone
cd tone

You can use the IRB console for direct access to all objects:


Lastly, there is a bin/demo script which displays the default styles for quick visual reference. This is the same script used to generate the screenshots shown at the top of this document.



To test, run: