Skip to content

Real-time interactions

uproot supports real-time communication between participants through WebSockets. This page covers broadcasting updates to multiple participants. For the basics of live methods (the @live decorator and uproot.invoke), see Live methods.

Two patterns: return values vs. notifications

There are two ways to send data back to the browser:

  1. Return values — Data goes back to the caller only
  2. Notifications — Data is pushed to one or more participants
@live
async def post_offer(page, player, price: float):
    player.my_offer = price

    # Broadcast to everyone (notification)
    notify(player, players(player.session), price, event="NewOffer")

    # Return to the caller only
    return {"posted": price}

Use return values for request-response patterns. Use notify for broadcasting to multiple participants.

Broadcasting with notify

notify sends data to one or more participants:

# Notify one player
notify(player, other_player, data)

# Notify all players in a group
notify(player, players(group), data)

# Notify all players in a session
notify(player, players(player.session), data)

# Notify everyone except the sender
notify(player, others_in_group(player), data)

The first argument is the sender (used to determine the current page context). The second is the recipient(s).

Custom events

Name your notifications with the event parameter:

notify(player, players(player.session), market_data, event="MarketUpdate")

Listen for specific events in JavaScript:

uproot.onCustomEvent("MarketUpdate", (event) => {
    refreshMarketDisplay(event.detail.data);
});

Default event handling

Without a custom event name, data goes to uproot.receive:

notify(player, other_player, "Hello!")
uproot.receive = (data) => {
    console.log(data);  // "Hello!"
};

send_to for server-initiated updates

Use send_to when you don't have a sender context (like in background tasks):

from uproot.smithereens import send_to

send_to(player, data)
send_to(players(session), data, event="StatusUpdate")

Example: live text observation

One participant types while another watches in real-time:

class Diary(Page):
    @live
    async def typed(page, player, text: str):
        observer = other_in_group(player)
        notify(player, observer, text)
// Writer
textarea.oninput = () => {
    uproot.invoke("typed", textarea.value);
};

// Observer
uproot.receive = (text) => {
    display.innerText = text;
};

See the observed_diary example

Example: collaborative drawing

Multiple participants drawing on a shared canvas:

class Draw(Page):
    @live
    async def stroke(page, player, points: list, color: str):
        player.session.strokes.append({"points": points, "color": color})

        # Send to others (not the sender)
        notify(player, others_in_session(player), {"points": points, "color": color}, event="NewStroke")
uproot.onCustomEvent("NewStroke", (event) => {
    drawStroke(event.detail.data.points, event.detail.data.color);
});

See the drawing_board example

Example: real-time market

A trading interface where participants post and accept offers:

class Market(Page):
    @live
    async def post_offer(page, player, price: float):
        player.my_offer = price

        # Broadcast updated market to everyone
        notify(player, players(player.session), list(player.session.offers), event="MarketUpdate")
        return {"posted": price}

    @live
    async def accept_offer(page, player, seller_id: str):
        seller = players(player.session).get(seller_id)
        price = seller.my_offer

        player.bought_at = price
        seller.sold_at = price
        seller.my_offer = None

        # Notify the seller directly
        notify(player, seller, price, event="OfferAccepted")

        # Update market for everyone
        notify(player, players(player.session), list(player.session.offers), event="MarketUpdate")
        return {"bought_at": price}
uproot.onCustomEvent("MarketUpdate", (event) => {
    renderMarket(event.detail.data);
});

uproot.onCustomEvent("OfferAccepted", (event) => {
    showNotification(`Your offer was accepted at ${event.detail.data}!`);
});

See the double_auction example

Summary

Feature Use case
notify(sender, recipients, data) Broadcasting updates to participants
notify(..., event="Name") Named events for specific handlers
send_to(recipients, data) Server-initiated updates (background tasks)
uproot.onCustomEvent("Name", fn) Listening for named events
uproot.receive = fn Default handler for unnamed notifications
others_in_group(player) All group members except sender
others_in_session(player) All session members except sender