Dynamic QR Code Generation Algorithm

Dynamic QR Code Generation Algorithm

Explains a direct C# implementation of the QR Code Model 2 encoding pipeline. Mode and version selection, Reed-Solomon codes, matrix placement, masking, and dynamic web generation are addressed.

Generating a QR code and implementing the QR Code standard are not the same operation. Passing text to an existing library and receiving an image uses only the final layer of the production pipeline. Developing a standards-compliant encoder requires selecting the data mode, calculating version capacity, generating Reed-Solomon error-correction codes, placing modules in the matrix, evaluating all eight masking options, and converting the resulting matrix into a lossless image.

I developed the QR code library that I have used on my personal website for years with this second approach. The page URL is encoded at runtime and the QR image is produced in milliseconds. In practical use, processing time remained close to serving a static image file. I therefore did not need to produce, store, name, and update a separate QR file for every page.

I used the same approach in my résumé documents. A person viewing the printed or PDF document could go directly to my personal website without manually entering a long address. Menus in restaurants and cafés are another natural use of QR codes. The real benefit is not merely opening the menu on a phone. While the printed QR code remains unchanged, prices, products, and descriptions can be updated on the server.

The result of an industrial requirement

QR Code was developed at Denso by Masahiro Hara and his colleagues in response to requirements for data capacity and rapid reading in automotive manufacturing. The one-dimensional structure of traditional barcodes carried limited information and required several barcodes to be used together on production lines. The development team worked on a two-dimensional symbol capable of carrying more data and being read quickly from different orientations. QR Code was announced in 1994, and one of its first important uses was in electronic Kanban processes in the automotive industry.

One of the defining design innovations was the position-detection patterns at three corners. The team searched for a black-and-white ratio unlikely to appear accidentally in printed documents and selected the 1:1:3:1:1 sequence. A scanner could identify this ratio from different angles and determine the symbol's position and orientation rapidly. The words Quick Response in the name reflect this objective.

QR Code became an AIM standard in 1997, a Japanese industrial standard in 1999, and an ISO standard in 2000. Denso Wave adopted a policy of not exercising its patent rights against applications conforming to the QR Code standard. This decision played an important role in allowing the technology to spread without dependence on a particular vendor or paid service. The current published symbology standard is ISO/IEC 18004:2024. It defines the fundamental rules from data encoding and error correction to symbol structure and production quality.

Technical position of the library

The library I developed does not produce a custom square pattern resembling QR. It implements the QR Code Model 2 encoding pipeline. Model 2 is the widespread format usually meant by the term normal QR code. In version 1, the symbol consists of 21 x 21 modules. Each new version adds four modules to the side length, reaching 177 x 177 at version 40. The relation is:

side length = 17 + 4 x version

Model 2 supports 40 versions and numeric, alphanumeric, byte, and Kanji data modes.

Architecturally, the source code belongs to the family of compact encoders that implement the ISO standard directly. The same overall sequence of selecting a data mode, finding the smallest suitable version, constructing error-correction blocks, scoring all masks, and selecting the best one appears in Project Nayuki's standards-oriented reference implementation. I reimplemented this approach for my C# web infrastructure, direct image generation, and short-URL use.

The library is not a general-purpose barcode package. It does not include decoding, symbol detection from camera images, Micro QR, rMQR, or multi-symbol structured append. Its purpose is to convert URLs and text of bounded length into a standard QR matrix with low latency. Narrowing the scope in this manner is one of the main reasons for its performance in live web use.

Selecting the data mode

QR Code does not have to encode every character with the same number of bits. Data containing only digits can be stored more densely in numeric mode. Data containing specified uppercase letters, digits, and limited punctuation can use alphanumeric mode. Other text is encoded in byte mode.

The library first tests the complete input for numeric mode, then for alphanumeric mode. If neither condition holds, the text is converted into UTF-8 bytes. Numeric mode stores three digits in 10 bits, while alphanumeric mode stores two characters in 11 bits. Because URLs on my personal website contain lowercase letters, most naturally use byte mode.

This approach selects one mode for the entire input. It does not use dynamic programming to search for optimal transitions among numeric, alphanumeric, and byte segments within the same text. For short URL values, the capacity gain from such an optimization is less important than the additional code and runtime complexity.

Although the code contains basic definitions for Kanji and ECI modes, the high-level text path does not use them actively. Turkish URLs are encoded reliably in UTF-8 byte mode. This is a suitable and predictable choice for web addresses.

Selecting version and error correction

The library first finds the smallest version capable of carrying the data. In my use, version 3 is the lower bound. Version 3 produces a 29 x 29 module matrix. Even when a very short URL could fit into a smaller version, this lower bound provides a more consistent visual size on web pages and documents.

A more important optimization is selection of the error-correction level. The algorithm initially accepts the low level. After finding a suitable version, it upgrades to the highest error-correction level that fits without increasing the symbol size. A more robust QR code is therefore obtained within the same matrix dimensions.

QR Code defines four error-correction levels:

  • L, approximately 7 percent
  • M, approximately 15 percent
  • Q, approximately 25 percent
  • H, approximately 30 percent

These ratios indicate approximate recovery capacity among total codewords. Data capacity decreases as error-correction strength increases, so the highest level is not always the best choice. Denso Wave states that level L can be used in clean environments, while Q or H may be suitable where contamination or damage is likely. Error correction is provided by Reed-Solomon codes.

Keeping the version fixed and increasing error correction as capacity permits is a good engineering decision, particularly for short URLs. The symbol does not grow, production cost does not increase meaningfully, and camera reading becomes more resilient.

Generating Reed-Solomon codes

After mode information and character count are added to the data bits, the stream is terminated, padded to a byte boundary, and extended with standard padding bytes until the required capacity is reached. The data is then divided into blocks determined by the version and error-correction table.

For each block, Reed-Solomon error-correction codewords are calculated in the finite field GF(2^8). The library performs field multiplication with bit-level polynomial arithmetic rather than precomputed logarithm and exponent tables. The reduction polynomial is 0x11D. The generator polynomial is constructed at runtime according to the required error-correction length.

This may not be the fastest possible general-purpose Reed-Solomon implementation. Under the URL limits, however, blocks are small, error-correction degrees are bounded, and total data volume is low. A short deterministic calculation instead of large helper tables keeps the library self-contained.

Data and error-correction codewords are interleaved among blocks. This helps prevent physical damage in one area of the symbol from destroying only one Reed-Solomon block. The resulting bitstream is placed in a zigzag pattern in pairs of columns, beginning from the lower-right region of the matrix.

Constructing function patterns

Not every cell in a QR matrix carries data. The encoder first places fixed structures required for the scanner to recognize and interpret the symbol:

  • Three position-detection patterns
  • Horizontal and vertical timing patterns
  • Version-dependent alignment patterns
  • Format bits carrying error-correction and mask information
  • Version information in higher versions
  • The fixed dark module

The library tracks data modules and function modules in two separate flat arrays. Using one-dimensional bool arrays instead of a two-dimensional array or pixel objects simplifies index calculation and provides regular memory access during matrix scans.

This distinction is important during masking. The mask is applied only to data and error-correction modules. Position, timing, alignment, and format patterns remain unchanged.

Evaluating the eight masks

If raw data bits are left unchanged in the QR matrix, long black or white runs, large single-color regions, and accidental shapes resembling position-detection patterns can appear. These can make optical reading more difficult. The QR standard therefore defines eight mask formulas.

The library applies each of the eight masks in turn and calculates a penalty score for every result. Evaluation considers four fundamental problems:

  1. Long horizontal and vertical runs of the same color
  2. Single-color 2 x 2 blocks
  3. Ratios resembling position-detection patterns
  4. Imbalance between dark and light modules

Penalty coefficients 3, 3, 40, and 10 from the standard are used. The mask producing the lowest total score remains in the final symbol. This stage is an important distinction between a simple QR producer and an encoder that considers symbol quality.

Avoiding separate allocation of eight matrices is also a deliberate optimization. A mask is applied to the same module array, scored, and reversed. Only the number of the best mask is retained.

When reevaluating the source version, mask expressions 5, 6, and 7 should be written with explicit parentheses independent of C# operator precedence and verified against standard test vectors. In a QR encoder, the mask formula and the mask number declared in the format field must match exactly for interoperability across readers.

Why it runs in milliseconds

The cost of a general QR encoder can be described by input length n, matrix side S, and codeword count C.

Selecting the data mode and constructing the bitstream:

Theta(n)

Reed-Solomon generation, depending on error-correction length, approximately:

O(C x E)

Matrix construction, data placement, masking, and image generation:

Theta(S²)

Because eight masks are evaluated separately, the constant factor increases but the asymptotic structure remains:

Theta(8 x S²) = Theta(S²)

Memory cost comes mainly from module and function arrays:

Theta(S² + C)

In my web use, input is limited to 60 bytes. Although the minimum version is 3, URLs usually remain in small versions. The algorithm therefore operates in a narrow and predictable range in the live system despite being theoretically variable in size.

No external process is started, no network service is called, no database query is performed, and no third-party QR service is used. There are no heavy operations such as cosine calculation, image analysis, or iterative optimization. The workload consists of small bit arrays, finite-field operations, and a few thousand matrix cells.

During image generation, only dark modules are drawn as rectangles. Scale is fixed and image size is small. Even bitmap construction through GDI+ therefore remains inexpensive within a normal web request. The millisecond-level production time I observed over the years is a result of this bounded and deterministic design.

If the same URL is requested repeatedly, HTTP caching, ETag, or a small application-level result cache can be added. Content generated dynamically on the first request can then be served on later requests through a path genuinely close to a static file. In my use, raw generation cost was already low enough that dynamic production alone did not create a problem.

Use on my personal website

Creating a separate QR file for every content page may initially appear faster. In practice, this creates a file-lifecycle problem. A new image must be produced when a page is added, renewed when a URL changes, and obsolete assets must be removed.

In dynamic production, the source is the current URL itself. The QR image is created at request time and added to the page. There is no separate mapping table between content routes and QR assets.

The QR code in this model is not a dynamic redirection service. The URL carried by the matrix is fixed. What is dynamic is server-side image generation at runtime. This distinction matters. No third-party dynamic QR provider, tracking link, or external redirection service is required. The domain name and access records remain under my control.

The QR code used in my résumé likewise created a bridge between the printed document and the current digital profile. Instead of fitting every project detail into the document, I could direct the reader to an updated web page. Persistence here comes not from the QR code itself, but from maintaining my own domain and URL structure over time.

The actual benefit for menus

The success of QR menus in restaurants and cafés does not come only from moving a printed menu onto a phone. The main value is separation of content from the physical carrier.

While the QR code on the table remains unchanged, the following information can be updated centrally:

  • Prices
  • Product descriptions
  • Allergen information
  • Unavailable products
  • Daily menus
  • Language options
  • Promotions

This reduces printing cost and the risk of presenting outdated information. A good QR-menu experience nevertheless requires the target page to open quickly, fit a mobile screen, and avoid requiring installation of an application. If the menu page is slower or more complex than the QR code, a well-generated symbol alone does not create a successful experience.

Using a short and direct URL is particularly important. As URL length increases, a higher version is required, modules become smaller, and reading from the same physical area becomes more difficult. A stable address on the business's own domain should be preferred over unnecessary tracking parameters and long redirect chains.

Printing and presentation boundaries

The source code defines an internal margin of one module. Denso Wave requires a four-module blank quiet zone around a standard QR Code. White space on a web page or a visual frame in a document may effectively provide additional space, and reading has been successful in my use. In a separately distributed PNG, PDF, or print output, however, the four-module quiet zone should be guaranteed within the image itself.

The image uses a module scale of three pixels. This is sufficient for screen presentation in many cases. For printing, physical module size, printer resolution, and scanning distance must be considered together. Denso Wave recommends at least four printer dots per module for stable printing.

I use dark navy rather than black for dark modules. QR Code does not absolutely require black, but strong contrast must exist between light and dark modules. A transparent-background image can work correctly on a white page while losing contrast on a colored or patterned background. The web layer should therefore guarantee a white background around the QR area.

PNG is more suitable than JPEG for presenting QR codes because it preserves sharp module edges losslessly. JPEG can produce blur and ringing artifacts at the boundaries of small blocks. Although the library changes the background to white for JPEG output, PNG is the more appropriate choice for normal use.

Another usage contract is the 60-byte data limit. The current implementation truncates longer input. For URL generation, silent truncation can produce a different or invalid address, so the caller must validate length beforehand. A general-purpose version should return an explicit error rather than truncate.

Value of the algorithm

The value of this library does not come from inventing a new QR symbology. It comes from implementing a well-defined international standard as a small, fast, and independent component suited to my own software architecture.

The implementation combines:

  • Selection of numeric, alphanumeric, or byte mode according to the data
  • Finding the smallest suitable version
  • Increasing error correction without increasing the symbol size
  • Local generation of Reed-Solomon codes
  • Direct placement of function patterns into the matrix
  • Scoring all eight masks
  • Flat and regular memory layout
  • Image generation without an external service or third-party dependency
  • A bounded URL-oriented operating domain

I therefore do not position the algorithm as a simple speed competition against general-purpose QR packages. A more accurate description is a web-specialized, bounded implementation of a complete Model 2 encoder with production characteristics.

Using it on my personal website for years, seeing its practical benefit in résumés, and reading it successfully with different devices showed that the development decisions held up in real use. Presentation time close to a static file did not come from one micro-optimization, but from correctly bounding the problem.

The success of QR Code itself rests on similar engineering. Position-detection patterns provide fast reading, Reed-Solomon codes provide damage tolerance, modes provide data density, and masks provide optical stability. My library combines these layers without omitting them, within a pipeline as small and fast as short-URL generation requires.

QR code for this page