DEV Community

ArtiDigital
ArtiDigital

Posted on

QR Code Architecture: Connecting 200 Guests to One Gallery

QR Code Architecture: How One Scan Connects 200 Guests to a Shared Gallery

Quick Answer: A QR code photo sharing system works by encoding a unique session URL into a scannable code. When guests scan it, their browsers join a WebSocket room tied to that session. Uploaded images broadcast to all connected clients instantly, creating a synchronized gallery without apps, accounts, or friction.

Introduction: The Problem with Wedding Photo Sharing

Three weeks ago, I watched a bride spend twenty minutes explaining a photo-sharing app to her grandmother. Download this, create an account, verify your email, join this album, enable notifications. The grandmother nodded politely and took zero photos for the rest of the night.

That friction is the enemy of event photo sharing. When you have 200 guests at a wedding, corporate event, or birthday party, you need a system that works for the tech-savvy niece and the uncle who still uses Internet Explorer. The solution? A single QR code that eliminates every barrier between wanting to share a photo and seeing it in the gallery.

In this post, I will break down the architecture we built to handle 200-plus concurrent guests uploading photos to a shared gallery in real-time. No apps. No accounts. No configuration. Just scan, snap, and sync.

QR code scanning animation

What Is a QR Code Session Architecture?

A QR code session architecture is a pattern where a machine-readable code encodes a URL containing a unique session identifier. Scanning the code opens a web application pre-configured with that session context, eliminating manual input and reducing onboarding friction to near zero.

The architecture has three core components:

  • Code Generation Layer: Creates QR codes embedding session-specific URLs

  • Session Management Layer: Tracks active rooms, participant counts, and expiration

  • Real-Time Sync Layer: Broadcasts uploads to all connected clients instantly

Unlike traditional photo-sharing apps that require account creation and album management, this approach treats the QR code as both the invitation and the authentication mechanism. The session ID in the URL is the only credential needed.

How QR Code Generation Works at Scale

We use the qrcode npm package, which sees 82 million monthly downloads, to generate codes server-side. The key decision is what goes into the code itself.

URL Structure and Session Encoding

Each QR code encodes a URL like:

https://gallery.app/s/abc123def

The /s/ prefix routes to our session handler, and abc123def is a cryptographically random 9-character alphanumeric string. We avoid UUIDs here because they make the QR code denser and harder to scan from a distance or at odd angles.

Error Correction and Scan Reliability

QR codes support four error correction levels: L at 7 percent, M at 15 percent, Q at 25 percent, and H at 30 percent. We use Level M as our default. Here is why: Level H creates larger, denser codes that scan worse from projectors and TV screens at events. Level M gives us 15 percent redundancy, enough to handle a smudged table tent or a wrinkled printed card without sacrificing scan speed.

Generation Pipeline

When an event organizer creates a gallery, our backend performs five steps:

  1. Generates a unique session ID
  2. Creates the session record in PostgreSQL with a 48-hour TTL
  3. Generates the QR code SVG with qrcode.toString()
  4. Uploads the SVG to our CDN for fast global delivery
  5. Returns the gallery URL and QR code URL to the organizer

The whole pipeline completes in under 200 milliseconds. Organizers get a shareable link and a printable QR code instantly.

Data flowing through network animation

Session Management: Handling 200 Concurrent Guests

Session management is where most QR code projects fail. A wedding is not a controlled environment. Guests arrive in bursts. Some stay connected for hours. Others scan the code, upload one photo, and close their browser.

WebSocket Room Architecture

We use Socket.IO, not raw WebSockets, for the sync layer because it handles fallbacks gracefully. If a guest corporate network blocks WebSockets, Socket.IO falls back to HTTP long-polling automatically.

Each session ID maps to a Socket.IO room. When a guest scans the QR code, four things happen:

  1. Browser loads the gallery page
  2. Socket.IO client connects to our server
  3. Server calls socket.join(sessionId)
  4. Server emits the current gallery state, the last 50 images, to that socket

The guest is now in sync with everyone else in the room.

Participant Counting and Limits

We track active connections per room using a Redis-backed counter. When a socket connects, we increment. When it disconnects, including on browser close, tab switch, or network drop, we decrement. This gives us real-time visibility into room occupancy.

Our soft limit is 250 concurrent connections per room. In practice, weddings rarely exceed 150 simultaneous uploaders because guests are eating, dancing, or talking, not constantly on their phones. The 250 limit is a safety rail, not a bottleneck.

Session Lifecycle and Cleanup

Sessions expire 48 hours after creation by default. We use Redis TTLs for automatic cleanup of active room data, and a nightly cron job archives uploaded images to cold storage before deleting the PostgreSQL session record.

This prevents abandoned sessions from consuming resources indefinitely. An organizer can extend a session manually, but the default expiration protects us from the set-it-and-forget-it problem.

Real-Time Gallery Synchronization

The magic happens when guest number 47 uploads a photo and guests number 3, 12, and 200 see it appear on their screen without refreshing.

The Upload and Broadcast Flow

When a guest selects a photo, the following occurs:

  1. Client resizes the image to 1200px width using Canvas API, reducing upload size by about 70 percent
  2. Client generates a client-side UUID for the image
  3. Image uploads to our presigned S3 URL
  4. On upload completion, client emits image:uploaded with the UUID and S3 key
  5. Server validates the session, stores metadata in PostgreSQL
  6. Server broadcasts image:new to all sockets in the room
  7. All connected clients append the image to their gallery DOM

Total latency from upload to broadcast is typically 300 to 800 milliseconds depending on image size and network conditions.

Handling Race Conditions

What if two guests upload simultaneously? Socket.IO processes events sequentially per room, so we do not get interleaved broadcasts. Each image:new event carries a server-assigned sequence number, and clients sort their galleries by this number. This guarantees consistent ordering across all devices regardless of network latency differences.

Offline Resilience

If a guest loses connection mid-upload, our client-side queue retries the upload automatically when connectivity returns. The client-side UUID prevents duplicate entries if the upload actually succeeded but the acknowledgement was lost.

Synchronized devices animation

Security Considerations for Open Galleries

An open QR code gallery has no passwords, no accounts, and no invite lists. That simplicity is the feature, but it also means we need other protections.

Rate Limiting by Session

Each session is rate-limited to 10 uploads per IP per minute. This stops a single guest from flooding the gallery without impacting anyone else experience. The limit is generous: 600 uploads per hour per person, which covers even the most enthusiastic photographer.

Content Moderation Pipeline

Every uploaded image passes through AWS Rekognition for content moderation. Inappropriate content is flagged and hidden from the gallery within 2 to 3 seconds of upload. Event organizers can review flagged content in a moderation dashboard, but guests never see it.

Session URL Obscurity

Our 9-character session IDs provide 52 bits of entropy. Brute-forcing a valid session ID is computationally infeasible during the 48-hour session lifetime. We do not rely on obscurity alone, it is one layer in a defense that includes rate limiting and moderation, but it means random scanning attacks will not find active galleries.

Performance Optimizations for 200 Plus Guests

Scaling to 200 concurrent users on a single gallery is not trivial. Here is what we learned:

Image Delivery: CDN and Lazy Loading

We serve images through CloudFront with aggressive caching. Thumbnails are generated at upload time, 300px width, for gallery grids, and full-resolution images load on click. This means 200 guests scrolling through a gallery are not hammering our origin server, they are hitting edge caches for tiny thumbnails.

Socket.IO Adapter Sharding

On our multi-node deployment, Socket.IO uses Redis as the adapter to broadcast events across server instances. Without this, guests connected to Server A would not see uploads from guests on Server B. The Redis adapter ensures room-wide broadcasts work regardless of which server handles the connection.

Database Query Optimization

Our gallery metadata queries use a composite index on session_id and created_at. With 200 guests uploading 5 photos each, that is 1000 rows per event. Even without pagination, PostgreSQL serves the full gallery state in under 5 milliseconds.

Common Mistakes When Building QR Code Systems

We made these mistakes so you do not have to:

  • Using UUIDs in QR codes: UUIDs create dense codes that scan poorly at distance. Use shorter, random alphanumeric strings.

  • Storing images in the database: Binary image data bloats PostgreSQL and slows backups. Use object storage like S3 with metadata references.

  • Ignoring WebSocket fallbacks: Corporate firewalls block WebSockets. Always implement HTTP fallback or use Socket.IO.

  • Client-side image resizing with CSS: Resizing with CSS still uploads the full-resolution file. Use Canvas API to resize before upload.

  • No expiration on sessions: Abandoned galleries accumulate forever. Always implement automatic cleanup.

Key Takeaways

  • A QR code session architecture encodes a unique URL that eliminates app downloads, accounts, and configuration for end users.

  • Error correction Level M balances scan reliability with code density for projector and print display.

  • Socket.IO rooms with Redis adapter enable real-time synchronization across multiple server nodes.

  • Client-side image resizing, CDN delivery, and thumbnail generation keep performance acceptable at 200 plus concurrent users.

  • Rate limiting, content moderation, and session expiration are essential for open, unauthenticated galleries.

  • Sequence numbers on broadcast events guarantee consistent gallery ordering across all connected devices.

Frequently Asked Questions

How many guests can connect to one QR code gallery simultaneously?

Our architecture supports 250 concurrent connections per gallery room. In practice, event activity patterns mean 150 to 200 simultaneous active users is typical, with brief spikes during key moments like cake cutting or first dances.

What happens if a guest loses internet connection during upload?

The client queues the upload and retries automatically when connectivity returns. Client-side UUIDs prevent duplicate entries if the upload succeeded but the acknowledgement was lost.

Do guests need to download an app or create an account?

No. The entire system works in a mobile browser. Scanning the QR code opens a web page where guests can immediately view and upload photos without registration.

How long are galleries and photos stored?

Default session lifetime is 48 hours. Organizers can extend this manually. After expiration, photos are archived to cold storage for 30 days before permanent deletion unless the organizer downloads them.

What image formats and sizes are supported?

We accept JPEG, PNG, and HEIC, converted to JPEG on upload. Client-side resizing reduces all images to 1200px width before upload. Maximum file size after resizing is approximately 800 kilobytes.

Can galleries be password-protected?

Yes, though we default to open galleries for maximum accessibility. Password protection can be enabled in the organizer dashboard, adding a 4-digit PIN that guests enter after scanning.

How does the system handle simultaneous uploads from multiple guests?

Socket.IO processes events sequentially per room. Each upload receives a server-assigned sequence number, and clients sort galleries by this number for consistent ordering regardless of network latency.

What QR code scanning libraries work best for this pattern?

We recommend html5-qrcode, which sees 5 million monthly downloads, for browser-based scanning. It supports camera selection, torch control, and falls back gracefully on older devices.

Is the gallery accessible on older phones?

Yes. The gallery interface uses progressive enhancement. Core functionality works on any device with a camera and a modern browser. Advanced features like real-time sync degrade gracefully if WebSockets are unavailable.

How much does it cost to run this architecture?

At our scale of hundreds of events per month, infrastructure costs run approximately 15 cents per event for compute, storage, and CDN. A single wedding with 200 guests and 1000 photos costs less than a cup of coffee to serve.

Conclusion

Building a QR code photo sharing system taught me that the best user experience is often the one with the fewest steps. No apps. No accounts. No friction. Just a code, a camera, and a gallery that updates in real-time.

The architecture is not magic. It is WebSockets, Redis, presigned S3 URLs, and some careful attention to error correction levels. But the result feels like magic to a grandmother who scans a code and sees her grandson wedding photos appear on her screen seconds after they are taken.

If you are building something similar, start with the session management layer. Everything else, QR codes, uploads, sync, depends on getting that right. And remember: the simplest interface for your users often requires the most thoughtful engineering underneath.

Building in public is how we learn. If you found this useful, check out our homepage for more technical deep-dives, or explore our article archive and project index for related work.

Top comments (0)