QR (Quick Response) codes have revolutionized how we share and access information in the digital age. From contactless payments to marketing campaigns, these two-dimensional barcodes encode data in a matrix of black and white squares that can be scanned by smartphones and dedicated readers. This comprehensive guide explores the technology, algorithms, and best practices for implementing QR codes in modern applications.
Understanding QR Code Structure
A QR code consists of several functional patterns that enable reliable scanning and data recovery:
Core Components
- Position Detection Patterns: Three large squares in corners that help scanners identify QR code orientation and boundaries
- Alignment Patterns: Smaller squares that correct for image distortion in larger QR codes
- Timing Patterns: Alternating black and white modules that help determine the coordinate system
- Format Information: Data about error correction level and mask pattern used
- Version Information: Indicates QR code version (size), present in version 7 and higher
- Data and Error Correction Modules: The actual encoded information with redundancy for error recovery
QR Code Versions and Capacities
| Version | Modules | Numeric | Alphanumeric | Byte |
|---|---|---|---|---|
| 1 | 21Γ21 | 41 chars | 25 chars | 17 bytes |
| 10 | 57Γ57 | 331 chars | 202 chars | 139 bytes |
| 20 | 97Γ97 | 1,056 chars | 644 chars | 442 bytes |
| 40 | 177Γ177 | 7,089 chars | 4,296 chars | 2,953 bytes |
Capacities shown are for error correction level L (Low - 7% recovery)
Error Correction with Reed-Solomon
QR codes use Reed-Solomon error correction codes, enabling them to be read even when partially damaged or obscured. Four error correction levels are available:
Level L (Low)
7% of data can be restored
Best for: Clean environments, maximum data capacity
Level M (Medium)
15% of data can be restored
Best for: General use, balanced capacity/reliability
Level Q (Quartile)
25% of data can be restored
Best for: Outdoor use, potential damage expected
Level H (High)
30% of data can be restored
Best for: Harsh conditions, logo embedding
π‘ Reed-Solomon Algorithm Basics
The algorithm works by treating data as polynomials and adding redundant "check symbols" that allow reconstruction of damaged data. The process involves:
- Divide message into blocks
- Treat each block as polynomial coefficients
- Generate error correction codewords using polynomial division
- Append error correction codewords to original data
- During decoding, use error correction to fix corrupted modules
Data Encoding Modes
QR codes support four primary encoding modes, each optimized for different data types:
1. Numeric Mode
Characters: 0-9 only
Efficiency: 3.33 bits per character
Example: "123456789" β Most efficient for numbers
2. Alphanumeric Mode
Characters: 0-9, A-Z (uppercase), space, $ % * + - . / :
Efficiency: 5.5 bits per character
Example: "HTTPS://KBC.SH" β Good for URLs
3. Byte Mode
Characters: Any 8-bit byte (ISO 8859-1 or UTF-8)
Efficiency: 8 bits per character
Example: "Hello, δΈη!" β Binary data, mixed languages
4. Kanji Mode
Characters: Japanese Kanji (Shift JIS encoding)
Efficiency: 13 bits per character
Example: "ζΌ’ε" β More efficient than byte mode for Japanese
QR Code Generation Implementation
Here's how to implement QR code generation in various programming environments:
JavaScript/Node.js Implementation
// Using the popular 'qrcode' library
const QRCode = require('qrcode');
// Basic QR code generation
async function generateQRCode(data, options = {}) {
try {
// Configuration options
const config = {
errorCorrectionLevel: options.errorCorrection || 'M',
type: options.type || 'image/png',
quality: options.quality || 0.92,
margin: options.margin || 4,
width: options.width || 400,
color: {
dark: options.darkColor || '#000000',
light: options.lightColor || '#FFFFFF'
}
};
// Generate QR code as Data URL
const qrDataURL = await QRCode.toDataURL(data, config);
return qrDataURL;
} catch (error) {
console.error('QR Code generation failed:', error);
throw error;
}
}
// Advanced: Generate with logo embedding
async function generateQRCodeWithLogo(data, logoPath) {
const QRCodeCanvas = require('qrcode');
const { createCanvas, loadImage } = require('canvas');
// Create canvas
const canvas = createCanvas(400, 400);
const ctx = canvas.getContext('2d');
// Generate QR code on canvas
await QRCodeCanvas.toCanvas(canvas, data, {
errorCorrectionLevel: 'H', // High for logo embedding
margin: 2,
width: 400
});
// Load and draw logo in center
const logo = await loadImage(logoPath);
const logoSize = 80;
const logoX = (canvas.width - logoSize) / 2;
const logoY = (canvas.height - logoSize) / 2;
// Draw white background for logo
ctx.fillStyle = 'white';
ctx.fillRect(logoX - 10, logoY - 10, logoSize + 20, logoSize + 20);
// Draw logo
ctx.drawImage(logo, logoX, logoY, logoSize, logoSize);
return canvas.toDataURL();
}
// Usage examples
generateQRCode('https://kbc.sh', {
errorCorrection: 'H',
width: 500,
darkColor: '#8b5cf6',
lightColor: '#faf5ff'
}).then(qrCode => {
console.log('QR Code generated:', qrCode);
});
Python Implementation
# Using the 'qrcode' library
import qrcode
from PIL import Image
def generate_qr_code(data, filename='qrcode.png', **options):
"""
Generate a QR code with customizable options
"""
# Create QR code instance
qr = qrcode.QRCode(
version=options.get('version', 1), # 1-40, or None for auto
error_correction=options.get('error_correction', qrcode.constants.ERROR_CORRECT_M),
box_size=options.get('box_size', 10),
border=options.get('border', 4),
)
# Add data
qr.add_data(data)
qr.make(fit=True)
# Create image
img = qr.make_image(
fill_color=options.get('fill_color', 'black'),
back_color=options.get('back_color', 'white')
)
# Save image
img.save(filename)
return img
def generate_qr_with_logo(data, logo_path, output_path='qr_with_logo.png'):
"""
Generate QR code with embedded logo
"""
# Generate base QR code with high error correction
qr = qrcode.QRCode(
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
# Create QR image
qr_img = qr.make_image(fill_color="black", back_color="white").convert('RGB')
# Open and resize logo
logo = Image.open(logo_path)
# Calculate logo size (max 30% of QR code)
qr_width, qr_height = qr_img.size
logo_size = min(qr_width, qr_height) // 4
# Resize logo
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
# Calculate position (center)
logo_pos = ((qr_width - logo_size) // 2, (qr_height - logo_size) // 2)
# Create white background for logo
from PIL import ImageDraw
draw = ImageDraw.Draw(qr_img)
draw.rectangle(
[logo_pos, (logo_pos[0] + logo_size, logo_pos[1] + logo_size)],
fill='white'
)
# Paste logo
qr_img.paste(logo, logo_pos, logo if logo.mode == 'RGBA' else None)
# Save result
qr_img.save(output_path)
return qr_img
# Usage
generate_qr_code('https://kbc.sh/tools/qr-code-generator',
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=15,
fill_color='#8b5cf6',
back_color='#faf5ff')
QR Code Scanning and Decoding
Implementing QR code scanning in web applications using modern browser APIs:
// Modern Web QR Scanner using jsQR library
import jsQR from 'jsqr';
class QRScanner {
constructor(videoElementId, canvasElementId) {
this.video = document.getElementById(videoElementId);
this.canvas = document.getElementById(canvasElementId);
this.canvasContext = this.canvas.getContext('2d');
this.scanning = false;
}
async startScanning(onScanSuccess, onScanError) {
try {
// Request camera access
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' } // Use back camera on mobile
});
this.video.srcObject = stream;
this.video.setAttribute('playsinline', true); // iOS compatibility
this.video.play();
this.scanning = true;
this.scan(onScanSuccess, onScanError);
} catch (error) {
console.error('Camera access denied:', error);
onScanError(error);
}
}
scan(onSuccess, onError) {
if (!this.scanning) return;
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) {
// Set canvas dimensions to match video
this.canvas.height = this.video.videoHeight;
this.canvas.width = this.video.videoWidth;
// Draw video frame to canvas
this.canvasContext.drawImage(
this.video,
0, 0,
this.canvas.width,
this.canvas.height
);
// Get image data
const imageData = this.canvasContext.getImageData(
0, 0,
this.canvas.width,
this.canvas.height
);
// Attempt to decode QR code
const code = jsQR(
imageData.data,
imageData.width,
imageData.height,
{
inversionAttempts: 'dontInvert',
}
);
if (code) {
// QR code detected
this.drawDetectionBox(code.location);
onSuccess(code.data, code);
}
}
// Continue scanning
requestAnimationFrame(() => this.scan(onSuccess, onError));
}
drawDetectionBox(location) {
// Draw corners of detected QR code
this.canvasContext.beginPath();
this.canvasContext.moveTo(location.topLeftCorner.x, location.topLeftCorner.y);
this.canvasContext.lineTo(location.topRightCorner.x, location.topRightCorner.y);
this.canvasContext.lineTo(location.bottomRightCorner.x, location.bottomRightCorner.y);
this.canvasContext.lineTo(location.bottomLeftCorner.x, location.bottomLeftCorner.y);
this.canvasContext.lineTo(location.topLeftCorner.x, location.topLeftCorner.y);
this.canvasContext.lineWidth = 4;
this.canvasContext.strokeStyle = '#00FF00';
this.canvasContext.stroke();
}
stopScanning() {
this.scanning = false;
if (this.video.srcObject) {
this.video.srcObject.getTracks().forEach(track => track.stop());
}
}
}
// Usage
const scanner = new QRScanner('video-preview', 'qr-canvas');
scanner.startScanning(
(data, code) => {
console.log('QR Code scanned:', data);
console.log('Full code object:', code);
// Process scanned data
processQRData(data);
},
(error) => {
console.error('Scanning error:', error);
}
);
// Data processing examples
function processQRData(data) {
// URL detection
if (data.startsWith('http://') || data.startsWith('https://')) {
window.location.href = data;
}
// Email detection
else if (data.startsWith('mailto:')) {
window.location.href = data;
}
// Phone number detection
else if (data.startsWith('tel:')) {
window.location.href = data;
}
// vCard contact detection
else if (data.startsWith('BEGIN:VCARD')) {
parseVCard(data);
}
// WiFi configuration
else if (data.startsWith('WIFI:')) {
parseWiFiConfig(data);
}
// Plain text
else {
displayText(data);
}
}
Practical Applications and Use Cases
π³ Payments & Finance
- β’ Mobile payment systems (Alipay, WeChat Pay)
- β’ Cryptocurrency wallet addresses
- β’ Invoice generation and tracking
- β’ Banking authentication tokens
π± Marketing & Engagement
- β’ Product packaging and labels
- β’ Event tickets and registration
- β’ Restaurant menus (contactless)
- β’ Promotional campaigns and coupons
π Authentication & Security
- β’ Two-factor authentication (2FA/TOTP)
- β’ WiFi network credentials sharing
- β’ Digital identity verification
- β’ Secure document signing
π¦ Logistics & Tracking
- β’ Package tracking and delivery
- β’ Inventory management systems
- β’ Asset tagging and monitoring
- β’ Supply chain transparency
Special QR Code Formats
// WiFi Configuration QR Code
const wifiConfig = {
ssid: 'MyNetwork',
password: 'SecurePassword123',
encryption: 'WPA' // WPA, WEP, or nopass
};
const wifiQRString = `WIFI:T:${wifiConfig.encryption};S:${wifiConfig.ssid};P:${wifiConfig.password};;`;
// vCard Contact QR Code
const vCardData = `BEGIN:VCARD
VERSION:3.0
FN:John Doe
ORG:KBC Grandcentral
TEL:+1-555-123-4567
EMAIL:[email protected]
URL:https://kbc.sh
ADR:;;123 Main St;San Francisco;CA;94105;USA
END:VCARD`;
// Event/Calendar QR Code
const eventData = `BEGIN:VEVENT
SUMMARY:Team Meeting
DTSTART:20251120T140000Z
DTEND:20251120T150000Z
LOCATION:Conference Room A
DESCRIPTION:Monthly team sync
END:VEVENT`;
// Email Composition QR Code
const emailQR = 'mailto:[email protected]?subject=Support Request&body=Hello, I need help with...';
// SMS/Text Message QR Code
const smsQR = 'SMSTO:+15551234567:Hello from QR code!';
// Generate QR codes for each format
generateQRCode(wifiQRString); // WiFi credentials
generateQRCode(vCardData); // Contact card
generateQRCode(eventData); // Calendar event
generateQRCode(emailQR); // Email template
generateQRCode(smsQR); // SMS message
Security Considerations
β οΈ QR Code Security Risks
- QRishing (QR Phishing): Malicious QR codes linking to phishing websites
- URL Manipulation: Short URLs hiding malicious destinations
- Data Injection: Embedded scripts or SQL injection attempts
- Physical Tampering: Stickers placed over legitimate QR codes
- Session Hijacking: QR codes exposing authentication tokens
Best Security Practices
- 1. Always Preview URLs Before Opening
Display the full URL to users before navigating, allowing them to verify the destination
- 2. Implement Content Security Policy (CSP)
Prevent execution of malicious scripts from scanned QR data
- 3. Validate and Sanitize Input
Never trust QR code data; validate format and sanitize content
- 4. Use HTTPS Only
Generate QR codes with HTTPS URLs to ensure encrypted connections
- 5. Set Expiration for Sensitive QR Codes
Time-limited codes for authentication, payments, or access control
- 6. Implement Rate Limiting
Prevent brute-force attacks on QR-based authentication systems
Secure QR Code Validation Example
// Secure QR code data validation
function validateQRData(data) {
const validators = {
url: (data) => {
try {
const url = new URL(data);
// Check for HTTPS
if (url.protocol !== 'https:') {
return { valid: false, error: 'Only HTTPS URLs allowed' };
}
// Check against whitelist
const allowedDomains = ['kbc.sh', 'trusted-domain.com'];
const isAllowed = allowedDomains.some(domain =>
url.hostname === domain || url.hostname.endsWith(`.${domain}`)
);
if (!isAllowed) {
return { valid: false, error: 'Domain not in whitelist' };
}
return { valid: true, type: 'url', data: url.href };
} catch {
return { valid: false, error: 'Invalid URL format' };
}
},
email: (data) => {
const emailRegex = /^mailto:([a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/;
const match = data.match(emailRegex);
if (!match) {
return { valid: false, error: 'Invalid email format' };
}
return { valid: true, type: 'email', data: match[1] };
},
phone: (data) => {
const phoneRegex = /^tel:(\+?[0-9]{10,15})$/;
const match = data.match(phoneRegex);
if (!match) {
return { valid: false, error: 'Invalid phone format' };
}
return { valid: true, type: 'phone', data: match[1] };
}
};
// Sanitize input
const sanitized = data.trim();
// Detect type and validate
if (sanitized.startsWith('http://') || sanitized.startsWith('https://')) {
return validators.url(sanitized);
} else if (sanitized.startsWith('mailto:')) {
return validators.email(sanitized);
} else if (sanitized.startsWith('tel:')) {
return validators.phone(sanitized);
}
// Plain text - sanitize for XSS
const sanitizedText = sanitized
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
return { valid: true, type: 'text', data: sanitizedText };
}
// Usage with user confirmation
function handleScannedQR(qrData) {
const validation = validateQRData(qrData);
if (!validation.valid) {
alert(`Security Warning: ${validation.error}`);
return;
}
if (validation.type === 'url') {
// Show confirmation dialog
const confirmed = confirm(`Open this URL?\n\n${validation.data}`);
if (confirmed) {
window.location.href = validation.data;
}
} else {
// Process other validated data
processValidatedData(validation);
}
}
Performance Optimization
π Size Optimization
- β’ Choose appropriate error correction level
- β’ Use numeric/alphanumeric modes when possible
- β’ Minimize data length (use URL shorteners)
- β’ Optimize version selection (smaller is faster)
π¨ Visual Optimization
- β’ Ensure sufficient contrast (3:1 minimum)
- β’ Add quiet zone (4 modules minimum)
- β’ Use vector formats (SVG) when possible
- β’ Avoid excessive customization (dots, gradients)
π± Scanning Optimization
- β’ Print at minimum 2Γ2 cm (0.8Γ0.8 inches)
- β’ Use matte finish to reduce glare
- β’ Test scanning from various distances
- β’ Consider lighting conditions
β‘ Generation Performance
- β’ Cache frequently generated codes
- β’ Use Web Workers for bulk generation
- β’ Implement lazy loading for displays
- β’ Pre-generate static codes at build time
Related Tools on KBC Grandcentral
QR Code Generator
Generate custom QR codes with various options and error correction levels
Barcode Generator
Create various barcode formats including UPC, EAN, Code128, and more
Base64 Encoder/Decoder
Encode and decode data in Base64 format for data transmission
URL Shortener
Shorten long URLs for more efficient QR code generation
Conclusion
QR code technology has evolved from inventory tracking to become an essential tool for contactless information sharing in our digital world. Understanding the underlying structure, encoding modes, error correction mechanisms, and security considerations enables developers to implement robust QR code solutions.
Whether you're building payment systems, marketing campaigns, authentication workflows, or logistics tracking, QR codes provide a reliable, standardized method for bridging physical and digital experiences. By following best practices for generation, security, and implementation, you can create QR code solutions that are both user-friendly and secure.
