Live auctions, scores and big audiences
Push the current bid or score to thousands of viewers at once, give newcomers the latest state instantly, and show a real “watching now” count.
Live auctions, sports scores, quiz shows, flash sales and live-stream comments have one thing in common: thousands of people watch the same thing at the same moment, and a second of delay changes the outcome. A bid that arrives late or a score that lags the broadcast sends people elsewhere.
What your users get
- The current bid, score or stock level the instant it changes.
- "12,408 watching now", a number that is real and moves.
- The latest state the moment they open the page, even mid-event.
Built with
- Cache channels (
cache-), so every newcomer gets the current price or score immediately, without hitting your database. - Large presence channels (
presence-large-), with up to 100,000 members, a live count and a sample of who is there. - Subscription counts (
pusher:subscription_count) for a simple viewer count on public channels. - Free fan-out: one bid published to 20,000 viewers counts as one message, not 20,000.
Step 1: publish each change once
Your server checks the bid or records the score, then publishes the new state to everyone:
app.post('/auctions/:id/bids', requireLogin, async (req, res) => {
const bid = await db.auctions.placeBid(req.params.id, req.user.id, req.body.amount); // rejects lower bids
await pusher.trigger(`cache-auction-${req.params.id}`, 'bid', {
amount: bid.amount,
bidder: bid.displayName,
endsAt: bid.endsAt,
});
res.json(bid);
});Always send the full state (the current amount and end time), not a difference: a viewer who missed an event is correct again with the next one.
Step 2: show it to the audience
const auction = pusher.subscribe(`cache-auction-${id}`);
auction.bind('bid', ({ amount, bidder, endsAt }) => {
price.set(amount);
feed.prepend(`${bidder} bid ${amount}`);
countdown.until(endsAt);
});The cached last bid arrives right after subscribing, so the price is never blank.
Step 3: a viewer count
For a count only, turn on Send subscriber counts in the app settings and bind to the count event on the same public channel:
auction.bind('pusher:subscription_count', ({ subscription_count }) => watching.set(subscription_count));Without writing code, the one-line embed script does the same: <span data-hibira-online="cache-auction-42">0</span> watching.
To show who is there too, use a large presence channel and @hibira-io/large-presence:
import { LargePresence } from '@hibira-io/large-presence';
const room = LargePresence.subscribe(pusher, `presence-large-auction-${id}`, { maxMembers: 50 });
room.on('ready', ({ count }) => audience.render(count, room.members));
room.on('change', ({ count }) => audience.render(count, room.members));The count stays exact while members' joins and leaves arrive as one small update per second, instead of one event per person.
Tips
- Validate every bid, answer or purchase on your server. Never let clients publish the state of the event themselves.
- Plan your connections, not your messages: 20,000 viewers are 20,000 concurrent connections, and the price depends on the peak. See pricing.
- For a sell-out moment such as a ticket release, publish stock levels every second or two rather than on every sale.
- Test with the real audience size before the big day: open many connections from a load-test tool and watch the app page in the portal.