How to Build a QR Code Generator from Scratch: A Complete Step-by-Step Guide

QR codes have become an integral part of modern digital interaction, bridging the physical and virtual worlds with a simple scan. Whether you’ve seen them on restaurant menus, product packaging, or business cards, these two-dimensional barcodes store information in a format that any smartphone camera can decode. Building your own QR code generator is not only a rewarding programming project but also gives you full control over customization, output formats, and integration into larger applications. Instead of relying on third-party APIs that may have usage limits or privacy concerns, a self-built generator allows you to generate unlimited codes offline, embed them in web apps, and even experiment with advanced features like adding logos or changing colors.

In this comprehensive tutorial, we will walk you through every step of creating a QR code generator from scratch. We will start by understanding the underlying technology—how QR codes structure data, what error correction means, and the different version sizes. Then we’ll move into the practical coding phase using Python (with the popular qrcode library) and also show you how to build a simple web-based interface with HTML, CSS, and JavaScript for those who prefer a browser-based tool. You’ll learn how to generate codes with custom colors, embed logos, choose error correction levels, and export results as high-quality PNG or SVG files. By the end, you’ll have a fully functional generator that you can deploy on your own website or use as a local utility. The guide is designed for intermediate developers, but even beginners can follow along if they have basic familiarity with Python or JavaScript. Let’s dive in.

Article illustration

Step 1: Understand the Anatomy of a QR Code

Before writing a single line of code, it’s critical to understand what a QR code actually contains. A QR code consists of black modules (squares) arranged on a white background. The pattern includes several functional zones: the finder patterns (the three large squares at the corners) that help scanners locate the code, timing patterns that define the grid, alignment patterns for version 2 and above, and the actual data area. The data is encoded using a Reed–Solomon error correction algorithm, which allows the code to be read even if parts are damaged or obscured. QR codes have 40 versions (sizes), from 21×21 modules up to 177×177 modules. Each version can store a different amount of data, and the capacity depends on the chosen error correction level.

There are four error correction levels: L (low, recovers 7% of data), M (medium, 15%), Q (quartile, 25%), and H (high, 30%). Higher levels allow you to cover part of the code with a logo or withstand more damage, but they reduce the maximum data capacity. For most use cases, level M or Q provides a good balance. Understanding these basics will help you when you later choose parameters in your generator. The actual encoding process involves several steps: data analysis, bit stream generation, error correction codeword calculation, interleaving, and module placement. Fortunately, libraries handle this complexity for you, but knowing the concepts prevents confusion when you see options like “box_size” or “border.”

Step 2: Set Up Your Development Environment

For the Python approach, ensure you have Python 3.8 or newer installed. You can download it from python.org. We will use the qrcode library (a pure Python QR code generator) along with Pillow for image manipulation if you plan to add logos or customize colors. Create a virtual environment to keep dependencies isolated:

python -m venv qr_env
source qr_env/bin/activate  # On Windows: qr_env\Scripts\activate
pip install qrcode[pil]

The [pil] extra installs Pillow automatically. For JavaScript developers, you don’t need any special setup if you’re building a client-side generator; you can use the browser’s Canvas API or a lightweight library like qrcode-generator from npm. If you prefer a Node.js environment, install it globally or locally via npm. We’ll cover both approaches in later steps. For now, make sure you have a code editor (VS Code, PyCharm, or even a simple text editor) and a terminal ready. You’ll also need a QR code scanner app on your phone (many free ones available) to test the generated codes.

Step 3: Generate Your First QR Code – Python Edition

Let’s start with the simplest possible generator. Create a new Python file called qr_basic.py and import the qrcode library. The core function is qrcode.make(), which accepts a string of data (URL, text, etc.) and returns an image object. You can then save it with .save().

Example code:

import qrcode

data = "https://www.example.com"
img = qrcode.make(data)
img.save("my_qr.png")

That’s it—run the script and you’ll have a PNG file named my_qr.png in your directory. But this basic version gives you little control. To customize, use the QRCode class instead. This class allows you to set version, error correction, box size, and border thickness. Then you can generate an image using make_image() with custom fill and background colors.

Example with customization:

import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import SquareModuleDrawer

qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_M,
    box_size=10,
    border=4,
)
qr.add_data("https://www.example.com")
qr.make(fit=True)

img = qr.make_image(fill_color="darkblue", back_color="white")
img.save("my_custom_qr.png")

You can also change the module drawer to use rounded squares, circles, or other patterns by importing styles from qrcode.image.styles. The StyledPilImage class allows you to pass a custom module drawer. However, for most purposes, the default square modules are fine. Experiment with different version values (1 to 40) and error_correction constants. Note that fit=True will automatically select the smallest version that fits your data.

Step 4: Add a Logo or Embedded Image

One of the most popular customizations is placing a logo in the center of the QR code. This requires using error correction level H (30% recovery) so that the logo’s obstruction doesn’t break the code. With the qrcode library and Pillow, you can overlay a logo image on top of the generated QR code. You must carefully position the logo so it doesn’t cover the finder patterns or timing patterns. The center area is safe as long as the logo is not too large (generally no more than 20% of the overall area).

Here’s a function that adds a logo:

from PIL import Image

def add_logo(qr_img, logo_path, logo_size=80):
    qr_width, qr_height = qr_img.size
    logo = Image.open(logo_path).convert("RGBA")
    logo.thumbnail((logo_size, logo_size), Image.LANCZOS)
    # Calculate center position
    x_pos = (qr_width - logo.size[0]) // 2
    y_pos = (qr_height - logo.size[1]) // 2
    qr_img.paste(logo, (x_pos, y_pos), logo)
    return qr_img

# Usage after generating basic QR with high error correction
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data("https://example.com")
qr.make()
img = qr.make_image().convert("RGB")
img_with_logo = add_logo(img, "logo.png")
img_with_logo.save("qr_with_logo.png")

Notice we used ERROR_CORRECT_H. Also, we convert the QR image to RGB before pasting to avoid alpha mode issues. The logo itself should ideally be a PNG with transparency (alpha channel) for a clean overlay. If your logo has a white background, you can mask it. This approach works perfectly for personal projects and even some commercial uses, but remember that overly complex logos can cause scanning failures—test thoroughly on multiple scanners.

Step 5: Build a Web-Based QR Code Generator Using HTML, CSS, and JavaScript

If you want a user-friendly interface that runs entirely in the browser, you can build a web-based generator using the qrcode.js library or the browser’s Canvas API. We’ll use qrcode.js (a popular lightweight library) because it handles all the heavy lifting. Start by creating an HTML file with a text input for data, a color picker for fill and background, a dropdown for error correction, and a button to generate. The result can be displayed as a Canvas element or an inline SVG.

First, include the library via CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>

Then set up your HTML form:

<input type="text" id="data-input" placeholder="Enter text or URL" value="https://example.com">
<select id="error-level">
  <option value="L">Low (7%)</option>
  <option value="M" selected>Medium (15%)</option>
  <option value="Q">Quartile (25%)</option>
  <option value="H">High (30%)</option>
</select>
<input type="color" id="fill-color" value="#000000">
<input type="color" id="back-color" value="#ffffff">
<button onclick="generateQR()">Generate QR Code</button>
<div id="qrcode"></div>

Now the JavaScript function that creates the QR code with the chosen options:

function generateQR() {
  const data = document.getElementById('data-input').value;
  const errorLevel = document.getElementById('error-level').value;
  const fillColor = document.getElementById('fill-color').value;
  const backColor = document.getElementById('back-color').value;

  // Clear previous QR
  document.getElementById('qrcode').innerHTML = '';

  const qr = new QRCode(document.getElementById('qrcode'), {
    text: data,
    width: 256,
    height: 256,
    colorDark: fillColor,
    colorLight: backColor,
    correctLevel: QRCode.CorrectLevel[errorLevel]
  });
}

This generates an inline Canvas with the QR code. To allow downloading, you can add a button that converts the Canvas to PNG using toDataURL(). For more advanced features like adding a logo or supporting SVG export, you might need different libraries (like qrcode-svg or custom Canvas drawing). The web-based approach is great for non-technical users and can be easily hosted on any static site. Remember to handle edge cases (empty input, too-long data) with appropriate messages.

Step 6: Output Formats – PNG, SVG, and More

Different use cases require different output formats. PNG is ubiquitous for web and print, but SVG (Scalable Vector Graphics) is superior for scalability and small file size. The Python qrcode library natively supports saving to PNG via PIL. To save as SVG, you need either the qrcode-svg library or you can generate SVG manually from the module matrix. Fortunately, the qrcode package includes an SVG image factory. Use qrcode.image.svg.SvgImage to produce clean SVG output.

Example of SVG generation in Python:

import qrcode
from qrcode.image.svg import SvgImage

qr = qrcode.QRCode()
qr.add_data("https://example.com")
img = qr.make_image(image_factory=SvgImage)
img.save("my_qr.svg")

This produces a compact SVG string. For more control (e.g., custom colors), you can subclass SvgImage or use SvgPathImage for path-based output. SVG files are perfect for logos, print materials, and responsive design because they scale without pixelation. In the browser, you can generate SVG using the qrcode library’s toSVG() method if available, or by drawing on a Canvas and exporting to SVG with third-party tools. Another format is EPS, which some professional printers require, but that’s less common.

The following table summarizes the most common output formats and their best use cases:

Format Best For Scalability Transparency File Size
PNG Web, social media, basic printing Limited (raster) Yes (alpha channel) Medium to large
SVG Logos, responsive web, high-res printing Infinite (vector) Yes Very small
EPS Professional print, Adobe Illustrator Infinite (vector) Limited Small

Step 7: Deploy Your Generator – Local or Cloud

Once your generator works locally, you might want to share it with others. For Python, you can create a simple Flask or FastAPI web server that exposes an API endpoint. When a user submits data via a form or a GET request, your server generates the QR code image and returns it as a downloadable file. Dockerize the app for easy deployment on platforms like Heroku, AWS, or DigitalOcean. For the JavaScript version, you can upload the HTML/CSS/JS files to any static hosting (Netlify, Vercel, GitHub Pages) and it will work immediately without a backend. This is the easiest route for a simple generator.

If you need to generate QR codes programmatically for batch processing (e.g., generating thousands of codes for event tickets), use Python scripts instead of a web interface. The script can read from a CSV file, generate codes with appropriate unique data (like UUIDs or serial numbers), and save them with meaningful filenames. You can also add logging and error handling. For high-traffic web applications, consider caching generated images or using a CDN to serve them. Always validate user input to prevent injection attacks (sanitize the data string).

Tips and Best Practices for Building a QR Code Generator

1. Always Use the Highest Practical Error Correction

Even if you don’t plan to overlay a logo, using error correction level H (30%) makes your QR codes more resilient to scanning issues like low light, dirty scanners, or minor damage. The trade-off is reduced data capacity, but for most URLs or short text, even version 1 with level H can hold enough. If you anticipate the code being printed on curved surfaces or placed in challenging environments, never drop below level M. Test your codes on at least three different scanner apps (e.g., Google Lens, QR Code Reader, camera default) to ensure readability.

2. Maintain Sufficient Quiet Zone (Border)

QR codes require a white border (quiet zone) around them to be scanned correctly. The standard is at least 4 modules wide. In the qrcode library, that’s the border=4 parameter. Many beginner generators omit this or set it too small, causing scanning failures especially when printed on dark backgrounds. If you design a code that will be placed on a colored surface, make sure the quiet zone is pure white or the lightest color of the surface. Some advanced designs embed the QR code into an image and try to incorporate the quiet zone as part of the artwork—but this risks breaking the standard.

3. Generate Codes at the Correct Physical Size

When you generate a QR code, the module size (box_size in Python) determines the physical size of each small square. For typical scanning (from a smartphone at 10-30 cm distance), a module size of at least 0.5 mm is recommended. For example, if your code is 25×25 modules (version 2), and you want it to be 2.5 cm (1 inch) wide, each module is 1 mm – fine. But if you generate a code using default box_size=10 and then resize in an image editor, you risk blurring. Always set the generation parameters so the final image is at least 2–3 cm (1 inch) across for reliable scanning. For large outdoor banners, scale up accordingly.

Frequently Asked Questions (FAQ)

Q1: Can I generate QR codes offline?

Yes, absolutely. The Python library works completely offline – it does not call any external API. The JavaScript library also runs entirely in the browser after the initial page load, meaning you can generate codes even without an internet connection once the page is cached. This is a key advantage over many online generators that require you to upload data to their servers. Building your own offline generator gives you privacy and reliability.

Q2: What is the maximum amount of data a QR code can hold?

The maximum capacity depends on the version (size) and error correction level. For version 40 (177×177 modules) with error correction L, a QR code can hold up to 7,089 numeric digits, 4,296 alphanumeric characters, or 2,953 bytes of binary data. With the highest error correction (H), these numbers drop to about 1,857 alphanumeric characters. The following table shows capacities for selected versions at error correction M (the most common middle ground):

Version Modules Numeric Alphanumeric Byte (binary) Kanji
1 21×21 41 25 17 10
5 37×37 224 137 93 57
10 57×57 652 395 271 164
20 97×97 2,080 1,256 864 528
40 177×177 5,592 3,376 2,335 1,424

Q3: Can I add a logo to a QR code and still have it scan?

Yes, but you must use the highest error correction level (H) because the logo occupies data area. The logo should be placed in the center of the code, and its size should not exceed approximately 20% of the total QR code area. Always test your logo QR code on multiple devices. Some scanners are more tolerant than others. Additionally, keep the logo simple with high contrast against the background of the code. QR code libraries like qrcode in Python can also generate “image QR codes” that embed an image directly, but that’s more advanced.

Q4: How do I change the color of a QR code?

Both Python and JavaScript generators allow you to specify fill and background colors. In Python, use the fill_color and back_color arguments in make_image(). In JavaScript via qrcodejs, use the colorDark and colorLight options. However, be cautious with colors – the scanner needs a high contrast between modules and background. Dark modules should be very dark (black, dark blue, dark green) and the background should be very light (white, light yellow). Avoid red/green combinations because many scanners interpret colors as grayscale. Always test colored QR codes.

Q5: What is the difference between static and dynamic QR codes?

A static QR code embeds the data directly. Once generated, you cannot change the destination URL or text without generating a new code. A dynamic QR code uses a short URL that redirects to the actual destination, which can be changed later. Static codes are simpler and never expire, while dynamic codes require a server to manage redirections but allow analytics (scan count, location) and the ability to update the link without reprinting. The generator you build here creates static codes; if you need dynamic codes, you would need a backend that stores mappings and serves redirects.

Conclusion

Building your own QR code generator is a practical and educational project that gives you deep insight into how these ubiquitous barcodes work. You’ve learned the foundational concepts of QR code structure and error correction, set up a development environment, generated your first codes with Python, added custom logos, and even created a web-based interface. You now know how to output both PNG and SVG formats, and you have a clear picture of deployment options from a simple local script to a full web service. The skills you’ve picked up—image manipulation with Pillow, client-side JavaScript libraries, and understanding data encoding—are transferable to many other projects. As next steps, consider extending your generator with features like batch processing from a spreadsheet, creating animated QR codes (by cycling frames), or integrating with a URL shortener for dynamic codes. Remember to always test your creations thoroughly because a QR code that doesn’t scan is useless. Now go ahead, generate your first code, and share it with the world. Happy coding!

sarah antaboga
Author: sarah antaboga

Leave a Reply

Your email address will not be published. Required fields are marked *