Skip to content

Visitor-to-visitor interaction: click avatar to open app-controlled drawer #124

@liebeskind

Description

@liebeskind

Summary

There is currently no SDK mechanism for one visitor to interact with another visitor's avatar. Clicking another player's avatar does nothing from an app/game perspective — there's no event fired, no webhook triggered, and no way to open an interactive UI scoped to that visitor-to-visitor interaction.

This is a fundamental gap for any social or multiplayer game built on Topia. Features like inventory trading, viewing another player's stats, sending challenges, gifting items, or any peer-to-peer interaction require hacky workarounds (e.g., "trading blankets" — dropped assets that both players must click to initiate a trade). These workarounds are confusing for users, fragile to implement, and break the spatial intuition of "I want to interact with that person."

A first-class visitor-to-visitor interaction system — click a player's avatar to open an app-controlled side drawer/panel — would unlock an entire category of social gameplay that the platform is uniquely positioned to support.

Current Behavior

  • Clicking another visitor's avatar has no app-programmable effect
  • There is no onVisitorClick webhook or event
  • Apps cannot open UI panels scoped to a specific visitor pair
  • The only way to implement trading or peer interaction is through shared dropped assets ("trading blankets") that both players must independently find and click

Current Workaround: "Trading Blanket" Pattern

1. Player A drops a "trading blanket" asset near themselves
2. Player A clicks the blanket → opens trade UI showing their inventory
3. Player B must walk to the blanket and also click it
4. Player B clicks the blanket → opens trade UI showing their inventory
5. Both players select items, confirm, and the server swaps inventories

Problems:

  • Requires both players to coordinate clicking the same physical asset
  • No guarantee both players are present — one might walk away
  • The blanket can be clicked by anyone, not just the intended trade partner
  • No real-time sync between the two players' views
  • Confusing UX for kids (ages 7-17): "Why do I need to click a blanket to trade with someone standing right next to me?"
  • Multiple blankets in the same area create confusion about which trade is which

Proposed API

1. Visitor Click Event / Webhook

When a visitor clicks another visitor's avatar, fire a webhook to the interactive app with both visitors' credentials:

// New webhook event: visitor-click
// Fired when Visitor A clicks on Visitor B's avatar
{
  event: "visitor-click",
  clickingVisitor: {
    visitorId: "visitor-a-id",
    profileId: "profile-a",
    displayName: "Player A",
    // ... standard visitor credentials
  },
  targetVisitor: {
    visitorId: "visitor-b-id",
    profileId: "profile-b",
    displayName: "Player B",
  },
  urlSlug: "my-world",
  // ... standard interactive credentials
}

2. Open Interactive Drawer for Visitor Pair

A new SDK method to open an interactive side drawer (similar to the existing asset-click drawer) but scoped to a visitor-to-visitor interaction:

// Server-side: open a drawer for the clicking visitor, scoped to the target
await visitor.openInteractionDrawer({
  targetVisitorId: "visitor-b-id",
  // The drawer loads the app's URL with both visitors' context
  // App receives both visitor IDs and can render pair-specific UI
});

3. Client-Side Context

The app's iframe would receive additional query parameters identifying the interaction:

// New query params available in the interaction drawer
const interactionType = searchParams.get("interactionType"); // "visitor-click"
const targetVisitorId = searchParams.get("targetVisitorId"); // "visitor-b-id"
const targetProfileId = searchParams.get("targetProfileId"); // "profile-b"
const targetDisplayName = searchParams.get("targetDisplayName"); // "Player B"

The app can then render context-specific UI — a trade interface, a profile view, a challenge prompt, etc.

4. Optional: Mutual Interaction (Both Drawers Open)

For features like trading that require both parties, an option to open drawers for both visitors simultaneously:

// Open paired drawers for both visitors (e.g., for trading)
await World.openMutualInteraction({
  visitorA: "visitor-a-id",
  visitorB: "visitor-b-id",
  urlSlug: "my-world",
  credentials,
  // Both visitors get a drawer opened with each other's context
});

Use Cases Unlocked

Use Case Current Workaround With This Feature
Item Trading Trading blanket asset both must click Click player → trade drawer opens for both
View Player Profile No way to do this Click player → see their level, stats, achievements
Challenge / Duel Drop a "challenge" asset, hope they click it Click player → "Challenge to a duel?" prompt
Gift Items Drop item near them, hope they pick it up Click player → "Gift this item?" with confirmation
Team Invite Broadcast toast, no targeting Click player → "Join my team?"
Inspect Inventory Not possible Click player → see what they're carrying
Social Reactions Expressions exist but aren't app-controlled Click player → app-specific reaction menu
Mentoring / Help No mechanism Click new player → offer to help, share tips

Lunch Swap Specific

In Lunch Swap, this would enable:

  • Direct item swaps — click a player, see their bag, propose a trade (both confirm)
  • Meal comparison — click a player who completed today, see their nutrition score vs yours
  • Streak flex — click a player to see their streak, level, and achievements
  • Targeted item gifting — click a player, select an item from your bag to give them

Security Model

  • Proximity requirement — The clicking visitor must be within a configurable radius of the target (e.g., 200 units). Prevents cross-map interactions.
  • Opt-in per app — Apps must register for the visitor-click event (similar to registering for asset interactions). Avatars only become clickable in worlds where an app has opted in.
  • Rate limiting — Prevent spam-clicking by rate-limiting visitor-click events (e.g., max 1 per target per 5 seconds).
  • Block/ignore — Respect any existing block/mute settings between visitors.
  • Consent for mutual interactions — For openMutualInteraction, the target visitor should see a prompt ("Player A wants to trade. Accept?") before their drawer opens. No forced UI popups.
  • App-scoped — The interaction drawer loads the same app that registered for the event. No cross-app interactions.

Implementation Suggestion

Platform Changes

  1. Click detection — When a visitor clicks another visitor's avatar in the Topia client, check if any interactive app in the world has registered for visitor-click events.
  2. Webhook dispatch — Fire the webhook to the app's server with both visitors' credentials.
  3. Drawer API — Extend the existing interactive drawer system to support visitor-scoped drawers (in addition to the existing asset-scoped drawers).
  4. Query params — Pass interactionType, targetVisitorId, targetProfileId, targetDisplayName to the app's iframe URL.

SDK Changes

// New event registration (in app configuration or programmatically)
await world.registerInteractiveEvent({
  event: "visitor-click",
  webhookUrl: "https://my-app.com/api/visitor-interaction",
  interactivePublicKey: key,
});

// New method on Visitor class
async openInteractionDrawer(options: {
  targetVisitorId: string;
  title?: string;        // Optional drawer title
  height?: number;       // Optional drawer height
}): Promise<void>;

// New static method for mutual interactions
static async openMutualInteraction(options: {
  visitorAId: string;
  visitorBId: string;
  urlSlug: string;
  credentials: Credentials;
  requireConsent?: boolean;  // Default: true — target must accept
}): Promise<{ accepted: boolean }>;

Type Definitions

interface VisitorClickEvent {
  event: "visitor-click";
  clickingVisitor: {
    visitorId: string;
    profileId: string;
    displayName: string;
  };
  targetVisitor: {
    visitorId: string;
    profileId: string;
    displayName: string;
  };
  urlSlug: string;
  distance: number;  // Distance between visitors at click time
}

interface InteractionDrawerOptions {
  targetVisitorId: string;
  title?: string;
  height?: number;
}

interface MutualInteractionOptions {
  visitorAId: string;
  visitorBId: string;
  urlSlug: string;
  credentials: object;
  requireConsent?: boolean;
}

Impact

This is arguably the single biggest unlock for social gameplay on the Topia platform. The SDK already excels at spatial presence (visitors in a world) and object interaction (clicking dropped assets). The missing piece is visitor-to-visitor interaction — the ability for players to directly engage with each other through app-controlled UI.

Every multiplayer game or social experience built on Topia would benefit: trading games, RPGs, social deduction games, educational activities with peer interaction, collaborative building, and more. The current workarounds (trading blankets, broadcast toasts, proximity-based auto-matching) are all compromises that degrade the user experience and increase developer complexity.

For the target audience (ages 7-17), direct avatar interaction is the most intuitive social mechanic possible: "I want to interact with that person, so I click on them." No blankets, no buttons, no coordination — just click.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions