rqrcode: one QR encoder in rqrcode_core, three renderers above it
A Ruby library that encodes QR Codes
At a glance
- What is it?
- rqrcode is a Ruby library that turns a string into a QR code and draws it as SVG, PNG or ANSI text, with the encoding work pushed down into rqrcode_core. The renderers are the reason to take it; the default error correction level is what you pay for them.
- Who is it for?
- Adopt rqrcode when the QR code belongs inside a Ruby application and you want the output format chosen per call, since as_svg, as_png and as_ansi all hang off the same object. Leave it out when the code has to be produced without Ruby, or when you only need the module matrix, because rqrcode_core covers that on its own.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 145 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Encoding runs in rqrcode_core, rendering runs in rqrcode
Two constants show up in this library and they do different jobs. The basic example builds an `RQRCode::QRCode.new` object, and that object is what carries the render calls. Underneath it, the advanced options block documents the constructor of a different class, `RQRCodeCore::QRCode`, and every sample line in that block calls the core class instead of the top-level one. Those options are introduced as coming from the underlying rqrcode_core, and the text says you may only need that library when you do not need the rendering options provided here.
That split is the design. Encoding, error correction and the module matrix live in one gem, while drawing the finished matrix as an image or a string lives in another. The same parameters, data, size, max_size, level and mode, are documented once, but the examples that pass them are written against the core namespace while the examples that render use the top-level namespace. Follow the text while writing code and keep track of which class you actually constructed, because the option list and the sample directly beneath it name different classes.
Lineage is documented too. The project is an adaptation of the Javascript library by Kazuhiko Arase, and it notes that the QR code is a trademark of Denso Wave inc. Minimum Ruby version is `>= 3.2.0`.
Two install paths, one Gemfile line, three lines of code
The README gives two ways in. For a bundled application you add one line to the `Gemfile`:
gem "rqrcode", "~> 3.0"Or you install the gem itself:
gem install rqrcodeRead that constraint before you trust it. The newest published version is 3.2.0, and `~> 3.0` admits any 3.x update above 3.0, so a fresh bundle tracks the current line rather than an older API.
The first call takes three lines and no options:
require "rqrcode"
qr = RQRCode::QRCode.new("https://kyan.com")
puts qr.to_sWhat comes back is a grid of x characters and spaces, because `to_s` defaults to `dark: "x"` and `light: " "` with no padding and no module sizing. Treat that output as a smoke test that encoding ran rather than as an image: the project itself calls it easy but unlikely to be readable, and sends you to the renderers for anything a camera has to read.
Payload shapes, module sizing and the level h default
Data is the first argument, and it is more permissive than a plain string. The constructor takes a string, a QRSegment, or an array of hashes that carry `data:` and `mode:` keys, which is how one payload gets split into segments with different encodings. When the input is a string, `mode` chooses between `:number`, `:alphanumeric` and `:byte_8bit`, defaulting to alphanumeric or byte_8bit depending on the data. That argument does nothing when data is not a string.
Segment arrays show the shape of it:
multi_qrcode = RQRCodeCore::QRCode.new([
{ data: 'foo', mode: :byte_8bit },
{ data: 'BAR1', mode: :alphanumeric }
])Sizing is automatic unless you intervene. `size` takes an integer module count and defaults to the smallest size that fits the data, while `max_size` defaults to `RQRCodeCore::QRUtil.max_size`, the ceiling the encoder will not cross. Error correction is the `level` argument, and the default is the most protective of the four: level :h restores 30% of a damaged code, against 25% for :q, 15% for :m and 7% for :l. Redundancy that large costs modules, so the default hands you a larger symbol in exchange for tolerance of damage. The advanced example steps away from that default on purpose, passing `size: 2` and `level: :m`.
as_svg writes standalone markup and counts pixels per module
`as_svg` returns a stand-alone SVG as a String. Its defaults are worth listing, because they are what you get when you pass nothing: offset at 0 pixels of padding, offset_x and offset_y both falling back to that offset, fill with no background colour, color 000 for the foreground, `module_size` at 11 pixels per module, `shape_rendering` set to `crispEdges`, `standalone` true, `use_path` false, `viewbox` false and an empty `svg_attributes` hash.
The sample writes those same defaults out longhand:
svg = qrcode.as_svg(
color: "000",
shape_rendering: "crispEdges",
module_size: 11,
standalone: true,
use_path: true
)Three of those carry more weight than the rest. Because `use_path` defaults to false, each dark module becomes its own `<rect>` element, and the option list says that drawing with `<path>` output shrinks the file significantly and becomes the default in a future version. `shape_rendering` defaults to `crispEdges`, a deliberate refusal of anti-aliased module edges. `viewbox` replaces the `svg.width` and `svg.height` attributes with `svg.viewBox` so CSS can scale the symbol, which is what you want when the same code appears at two sizes on one page.
Mind the padding defaults while you are in there. Passing `offset_x` alone leaves `offset_y` on the offset value, and passing nothing at all leaves the modules with no margin around them.
as_png runs through ChunkyPNG with two sizing algorithms
`as_png` hands back a `ChunkyPNG::Image`, so PNG output rides on the ChunkyPNG gem instead of being drawn by rqrcode itself. Colors take a `ChunkyPNG::Color` and accept an array for the multi-argument forms, with fill defaulting to white and color to black. When you write to a file, four ChunkyPNG settings pass through: `color_mode`, defaulting to 'ChunkyPNG::COLOR_GRAYSCALE', `bit_depth`, defaulting to 1 bit, plus `interlace` and `compression`, where compression takes a value from 0 to 9 or a Zlib constant such as Zlib::BEST_COMPRESSION.
Those defaults do not quite agree with each other. `bit_depth` is documented as applying only to indexed images, and the documented default color_mode is grayscale rather than an indexed mode, so the headline 1-bit figure does not describe what a default call produces.
Sizing splits into two algorithms. The original one creates an image where one pixel equals one module and then resizes it, which the README says can result in blurry and hard to scan images. The Google Chart API inspired algorithm recomputes the module size so the code fits the requested image size, and it runs when you give no options or use the newer `size` option, which defaults to 120 pixels. `border_modules` sets the white border around the modules and defaults to 4:
png = qrcode.as_png(
bit_depth: 1,
border_modules: 4,
color_mode: ChunkyPNG::COLOR_GRAYSCALE,
color: "black",
file: nil,
fill: "white",
module_px_size: 6,
resize_exactly_to: false,
resize_gte_to: false,
size: 120
)
IO.binwrite("/tmp/github-qrcode.png", png.to_s)For the other algorithm the list names `module_px_size` and `border`, and says an image at 1px per module is created first and resized afterwards, starting from 120x120 pixels.
Padding comes in modules, in pixels and at zero by default
Padding is the quiet zone, and this library exposes it under three names across two renderers. In `as_svg` it is offset, offset_x and offset_y, all measured in pixels and all defaulting to 0. In `as_png` it is `border_modules`, measured in modules and defaulting to 4, or `border`, a pixel thickness that belongs to the original sizing algorithm rather than the Google inspired one.
The unit is the whole point. A margin measured in modules grows with the symbol while a margin measured in pixels does not, so a code that scans cleanly at 120 pixels can end up with the wrong margin inside a print job if you generated it with the pixel option. That hazard is called out in capitals against `border_modules`, in a line telling you not to touch the option unless you know what the quiet zone needs of QR codes. Nothing equivalent is said about the SVG offsets, which ship at zero.
Two workable choices then. Leave the defaults so each renderer handles its own margin, or set one value per renderer and keep the units straight when the same payload is drawn at more than one size.
The PNG sample uses two keys the option list never defines
Return to that PNG sample and read its keys against the options documented above it. The example sets `resize_exactly_to: false` and `resize_gte_to: false`, and neither key appears in that list, which covers `size`, `border_modules`, `module_px_size` and `border` and explains when the Google inspired sizing takes over. Two switches get used in sample code without being defined anywhere in the text, and both are turned off, so the example tells you they exist without telling you what setting them true would do. If neither documented algorithm fits your sizing needs, that gap is the first place to look.
`as_ansi` is the narrowest renderer of the three. It returns a String carrying ANSI colour codes, and its documented option list opens with `light`, a foreground ANSI code defaulting to `"\033[47m"`. Output like that only means something on a terminal, which makes it a console convenience rather than something to hand to a browser or a print pipeline.
Taken together the three are unevenly covered. The SVG list is detailed enough to configure from, the PNG list is detailed except for the two sizing keys its own example uses, and the ANSI list is the thinnest.
When rqrcode_core alone is the better fit
If you never draw the result, the top-level library is more than you need. The advanced options section says the generation parameters come from rqrcode_core and that you may only need that library when you do not want the rendering options and just want the data structure. Core accepts the same data, size, max_size, level and mode arguments and stops at the matrix, so a project with its own drawing code has nothing to configure beyond that.
Repository layout matches the same shape: `lib/` for the code, `spec/` beside a `.rspec` file, `benchmark/`, `images/` holding the two sample renders the README embeds, `bin/`, `rqrcode.gemspec`, `Gemfile`, `Gemfile.lock`, `.standard.yml` for code style and `AGENTS.md`. A `_config.yml` sits among them, which is the settings file a Jekyll site uses, so the project page is built as a static site. No command line interface appears anywhere in the README, so if you wanted a shell tool that prints a code from a URL, this is a library and not that.
Two facts to settle before you commit: the code is distributed under the MIT licence, and the last push was on 2026-06-01.
Editorial conclusion
Adopt rqrcode when the QR code belongs inside a Ruby application and you want the output format chosen per call, since as_svg, as_png and as_ansi all hang off the same object. Leave it out when the code has to be produced without Ruby, or when you only need the module matrix, because rqrcode_core covers that on its own. Before committing, render one code from your real payload and scan it, leave border_modules at 4 unless you have read up on quiet zones, and confirm your application already runs Ruby 3.2.0 or newer.
Frequently asked questions
What Ruby version does rqrcode need?
The project states a minimum Ruby version of >= 3.2.0. Installing by hand means running gem install rqrcode, and a bundled application adds gem "rqrcode", "~> 3.0" to its Gemfile.
What is the difference between rqrcode and rqrcode_core?
The generation options come from the underlying rqrcode_core, and rqrcode adds the rendering options on top. You may only need the core library when you do not need those renderers and just want the data structure.
How do I turn a rqrcode QR code into a PNG?
Call as_png on the code object and you get a ChunkyPNG::Image back, which you can write out with IO.binwrite. The Google Chart API inspired sizing runs by default and produces an image of 120 pixels.
Why is the QR code I generated with rqrcode not scanning?
Two documented causes come first. The original PNG sizing algorithm can result in blurry and hard to scan images, and the option list warns against changing border_modules without knowing the quiet zone needs of QR codes.
Which output formats does rqrcode produce?
The README covers as_svg, which returns a stand-alone SVG as a String, as_png, which returns a ChunkyPNG::Image, and as_ansi, which returns a String carrying ANSI colour codes for a terminal.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/whomwah-rqrcode)Was this useful?