Full screen, safe areas and HUD corners for PlayDrop games
Make a PlayDrop game fill the screen, keep its HUD inside CSS safe areas and clear of the host's controls using the SDK's measured reservedCorners.
PlayDrop gives your game the whole screen: the website's game page has its own full-screen control and the apps play games full screen. On phones, PlayDrop places small Pause and Audio controls (and a voice control during a party) over the top corners of the game. The SDK tells you exactly where they are, so the HUD can stay clear of them without wasting space.
Fill the viewport
Draw edge to edge and stop the page from scrolling, zooming or selecting text:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
<style>
html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: #000; }
body { touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
canvas { display: block; width: 100vw; height: 100dvh; }
</style>Size the canvas from its displayed size times devicePixelRatio, and redraw on resize; windows change size on desktops, tablets, foldables and when the phone rotates. Background art and the play field go everywhere, including under notches and the host's controls. Only interactive and readable HUD elements need to avoid them. You do not need your own full-screen button.
Keep the HUD clear
Two things can cover the edges:
- Hardware safe areas, such as a notch or the home indicator, from CSS
env(safe-area-inset-top|right|bottom|left). - The host's controls, from
sdk.host.reservedCorners:{ topLeft: { width, height }, topRight: { width, height } }, in CSS pixels measured from the top-left and top-right corners of the game's viewport.
Avoid the union of the two, never their sum. A top-left HUD element can sit below the corner rectangle or beside it; either way, use the larger of each clearance:
const root = document.documentElement.style;
function layoutHud(corners) {
const left = corners?.topLeft ?? { width: 0, height: 0 };
const right = corners?.topRight ?? { width: 0, height: 0 };
root.setProperty("--pd-tl-h", `${left.height}px`);
root.setProperty("--pd-tr-h", `${right.height}px`);
}
layoutHud(sdk.host.reservedCorners);
const stop = sdk.host.onReservedCornersChange(layoutHud);.score { position: absolute; left: max(12px, env(safe-area-inset-left));
top: max(12px, env(safe-area-inset-top), var(--pd-tl-h, 0px)); }
.lives { position: absolute; right: max(12px, env(safe-area-inset-right));
top: max(12px, env(safe-area-inset-top), var(--pd-tr-h, 0px)); }- A zero width and height means nothing covers that corner, for example on the desktop website, where the controls sit outside the game.
nullmeans an older host that does not report corners: keep your existing layout with safe areas.- The values come from the controls actually on screen, such as touch targets of at least 44 points on phones, and change with rotation, resizing and parties. Read them; never hardcode a size or reserve a full-width top band.
Unsubscribe with the returned function when you tear the game down.
Orientation and surfaces
Declare where the game works in catalogue.json: surfaceTargets lists desktop, mobileLandscape and mobilePortrait, and primarySurface names the one the game is designed for. Enable only what you have tested. A portrait game shown on a wide screen should still fill it with a coherent background instead of black bars, and an Instant game must be portrait-first.
Input on touch screens
Make controls comfortable for thumbs: at least 44 CSS pixels, away from the reserved corners and the screen edges where system gestures start. Use pointer events, call preventDefault() on game touches, and support keyboard and mouse on desktop. Controllers are covered in local parties and controllers.