Build a live auction or shopping room
A salesroom combines a host stage, a fast stateful console, and an audience that may watch without joining the realtime floor. Keep those paths explicit: the auction backend is authoritative for bids and settlement, while MediaSFU is authoritative for room media.
Build this: compare the runnable starter and its platform evidence. View source: MediaSFU Live Auction.
The journey
- The host creates a short-lived salesroom through the backend.
- The host mints a bidder invitation or a viewer invitation.
- A bidder redeems once and joins the realtime room; a viewer can receive the program through HLS without paying for a room participant.
- The host opens each lot, accepts ordered bids, applies the reserve and anti-snipe rules, and settles the lot.
- The host ends the auction. The backend deletes the MediaSFU room and clears the auction state before reporting success.
Invitation and authority
The auction backend is authoritative for lot state, bid ordering, settlement, and invitations. Mint separate opaque, expiring grants for bidder and viewer roles. A bidder joins the realtime floor; an HLS viewer receives only the program. Keep MediaSFU credentials and bid authority on the server, and bind each grant to the salesroom and intended role.
Room lifecycle
Create a room only after the host starts the salesroom. Keep lot progression and room media as separate state machines: a bid can settle while a camera is off, and a viewer can watch without joining the floor. End the lot, stop any program output, leave/end the room, and clear the auction record only after upstream teardown is confirmed.
Primary and mini media
Put the active lot, countdown, leading bid, and host controls beside the live
stage. Give every contributor a card, including an audio-only or camera-off
identity. Screen share should take the primary stage without mirroring or
cropping; cameras use cover, and the secondary floor remains visible as
compact cards or a rail.
Keep room audio independent from the visible video card. A viewer delivered by HLS should not receive the private realtime floor audio unless your product explicitly admits that viewer to the room.
Media identity
Resolve the member identity before producer lookup. Screen share is the primary source when active; otherwise use an identified remote camera and then the local camera. Keep the HLS program, bidder floor, and audio-only participant as separate identities. A prepared card or lot state is not proof that media is publishing.
Remote audio
Mount floor audio independently from video cards. An HLS-only viewer receives program audio, not private realtime floor audio, unless your backend admits that viewer to the room.
Responsive layout
Keep the active lot, leading bid, and settlement action adjacent to the stage. At narrow widths, move bidder cards into a compact rail and stack the lot console beneath the program. Do not hide the independently mounted floor audio when the video rail collapses.
Bid and room idempotency
Use separate keys for separate authorities. A room create or invite redemption
uses an HTTP Idempotency-Key and reuses it only for an exact retry. A bid uses
its own participant-scoped bid key. Do not use the bid key to protect room
creation, and do not put either key in an invitation URL.
const createKey = crypto.randomUUID();
await fetch('/api/auctions', {
method: 'POST',
headers: { 'content-type': 'application/json', 'Idempotency-Key': createKey },
body: JSON.stringify({ hostName: 'Auctioneer' }),
});
The backend stores only the minimum auction state it needs, hashes or scopes participant capabilities, and keeps MediaSFU account credentials outside the browser. Opaque bidder/viewer grants should expire and be single-use.
Release boundaries
This pattern does not include payment processing, inventory transfer, tax, or legal settlement records. Add those as separately authorized backend systems. Before release, test a real host and bidder, a viewer path if offered, bid ordering, lot settlement, room end, page-exit cleanup, and retry behavior.
Failure states
Reject malformed or replayed idempotency keys, stale invitations, out-of-order bids, unavailable media, and failed room end as product states. Keep the current lot readable while a scoped request retries, and tell the operator whether to retry the bid, reopen the lot, recreate an invite, or end the room.
Cleanup
Stop HLS or realtime output, stop local publications, remove room listeners and timers, leave/end the room once, and clear active lot and invitation state. Do not report a successful end until the backend confirms that the room and auction residue are gone.
Product evolution
An earlier salesroom could make prepared bidder cards look live and let auction state outlast room teardown. The stable lesson is to use current published media and backend-confirmed lot state as separate authorities, and to clear both domains on an explicit, idempotent end.
Choose and test your platform
The React implementation is the most complete reference for a two-browser room, synchronized bids, explicit end, and cleanup. Angular, Vue, React Native, Expo, Flutter, Kotlin, and Unity ports are also available. Test the chosen port on its target browser or device, including host/bidder media, bid ordering, recovery, and cleanup. Add payment, inventory, tax, and legal settlement as separate backend systems when your product requires them.
Related implementation
Open the canonical route at /showcases/live-auction. The standalone
MediaSFU Live Auction starter
has React, Angular, Vue, React Native, Expo, Flutter, Kotlin, and Unity port
boundaries. Begin with the React browser room for bid synchronization and
cleanup, then test media and recovery in the exact native or Unity build you ship.