MINIGAMES · EMBED KIT

Minigames Lobby

A self-contained minigames lobby — game catalog plus a live leaderboard — that you drop into any site with a single <iframe>. No SDK, no build step. The UI adapts to the width of the frame you give it, so it looks right in a phone-width sidebar or a full-width page.

Container-aware

Lays out from the width of the iframe, not the visitor's screen. Single column when narrow, a two-column dashboard past ~790px — powered by CSS container queries.

Zero-config embed

One <iframe> pointed at the /lobby/ path. No SDK, no bundler, no build step on your side.

Static & portable

Prerendered static export — plain HTML/CSS/JS. Host it on S3 or any CDN. No server, no runtime dependencies.

Live preview

Both frames below are real embeds of the deployed /lobby/ route, each sized only by its wrapper. Resize your browser to watch the fluid one reflow — the layout follows the frame's width, not the screen.

Fluid — fills its containertwo columns ≥ 790px
Phone390px

In a narrow frame

Same component, same code — the layout responds to the frame's own width:

  • Between 448px and 790px the leaderboard moves below the game catalog, so the two stack into a single column.
  • Below 448px the phone layout takes over: the catalog becomes a one-card carousel.
  • LOBBY and LEADERBOARD tabs then switch between the two.
  • Nothing is cropped — the frame scrolls when the layout needs more height than you gave it.

Embed it

Point the iframe at your deployed lobby URL. That's the whole integration.

HTML
<iframe
  src="https://your-host.example.com/lobby/"
  title="Minigames Lobby"
  width="100%"
  height="720"
  loading="lazy"
  style="border: 0; display: block;"
></iframe>
React / JSX
export const MinigamesLobby = () => (
  <iframe
    src="https://your-host.example.com/lobby/"
    title="Minigames Lobby"
    loading="lazy"
    className="block h-[720px] w-full border-0"
  />
);
HTML — in a narrow slot
<!-- Narrow slot? The lobby detects its own width and
     switches to the phone layout — no config needed. -->
<div style="width: 380px">
  <iframe
    src="https://your-host.example.com/lobby/"
    title="Minigames Lobby"
    width="100%"
    height="720"
    loading="lazy"
    style="border: 0"
  ></iframe>
</div>

Events

You cannot see inside the frame, so the lobby tells you when a player starts a game and when they come back out. Two postMessage events on window.parent — no SDK, nothing to opt into.

game-opened
A player tapped PLAY NOW and the game is taking over the frame. Carries game.title.
game-closed
They left the game and the lobby is back. Fires however they got out — the close control, or Escape in the windowed layout.
JavaScript — listen on your page
window.addEventListener("message", (event) => {
  // Only trust messages coming from the host you embedded.
  if (event.origin !== "https://your-host.example.com") {
    return;
  }

  const message = event.data;
  if (message?.source !== "oddin-minigames") {
    return;
  }

  if (message.type === "game-opened") {
    // A player started message.game.title: pause your own carousel,
    // hide a sticky header, hold off a live refresh …
    return;
  }

  if (message.type === "game-closed") {
    // They are back in the lobby — undo whatever you did above.
  }
});

Every message carries source: "oddin-minigames", so it is safe to filter on that and ignore the rest of your page's postMessage traffic. The target origin is "*" — the lobby is a static export and does not know who embeds it — so check event.origin against your lobby host, as the snippet does.

Sizing & behavior

Width
Set it to the width of your slot — width="100%" is usually what you want. From about 790px the catalog and the leaderboard sit side by side; below that they stack into one column, and below 448px you get the phone layout.
Height
An iframe can't grow to its content on its own, so give it a height: 720 is a good default, and the lobby fills whatever it gets. The two-column layout needs about 700px to fit; the stacked one about 1240px.
Scrolling
A frame shorter than the layout needs scrolls inside itself — your page never grows to fit the embed. Nothing else is required: the lobby has no chrome of its own, so no permissions to grant.
Path
Reference the deployed /lobby/ URL — mind the trailing slash: the static export serves /lobby/index.html.
Language
Add ?lang= after the trailing slash — /lobby/?lang=ru — and the lobby, its game tiles, the games' loading screens and the games themselves follow it. en (the default) and ru are supported; anything else falls back to en. Game titles are artwork and stay as they are.
Session
Put the player's session token in the URL's hash — /lobby/#session=<token> — and the lobby passes it on to the game it starts in that game's own hash. Keeping it out of the query means it is never sent to a server, so it stays out of access logs and referrers on both hops; combine it with ?lang= as /lobby/?lang=ru#session=<token>. Without it the lobby and the games still run, unauthenticated. The token is read once when the lobby loads, and changing only the hash does not reload a frame — to hand over a new one, reload the iframe itself (remount it, or call contentWindow.location.reload()).
Accessibility
Always set a title. Add loading="lazy" so the embed never blocks your page load.

Deployment. The lobby ships as a static export (plain HTML/CSS/JS). Host it on S3 static website hosting or any CDN — index document index.html, error document 404.html. Serve /lobby/ from the same host you embed from (or reference its absolute URL) and you're done.