Designing your own tablet
Our sample app has every feature at once, making it a bad interface. It exists to exercise the whole protocol on real hardware: ten controls, a hidden admin menu, a language popover, a rating card, an error banner. No real installation wants all of this, and those that mimicked it ended up worse than ones built from scratch.
Using the library directly is the how; this page is the what. Take the library and the patterns, then build only what your visitors need — for most kiosks, three or four controls. What we would build today:
[[1-one-huge-start-button]] == 1. One huge Start button
If visitors start the conversation from the tablet, make Start the only thing on the idle screen. Make it enormous, centre it, and animate it so passers-by see the kiosk is waiting. Ours is a card in a row of four; yours should be the screen.
It is one command and one state key:
sends |
|
reads |
|
goes quiet while |
|
Keep those last two. A Blueprint connect takes a few seconds. Without sessionBusy, a visitor taps
four times and starts four conversations.
|
Tip: do not make the button the only way in. The best installations start the conversation when
somebody approaches, keeping the button as a fallback. Presence detection — computer vision, a
depth camera, or proximity sensors; an Intel RealSense depth camera has been the most reliable of
these for us — hooks in either way. A sensor inside Unreal fires a Blueprint event
straight into the |
You do not need our component for this. If you do use it, the press animation lives on the inner button precisely so you can animate the outer element yourself:
pk-button#btnSession {
--pk-control-min-height: 45vh; /* make it huge */
--pk-font-size: 42px;
animation: invite 2.2s ease-in-out infinite;
}
@keyframes invite {
50% { transform: scale(1.04); }
}
/* Somebody will be looking at this screen all day. */
@media (prefers-reduced-motion: reduce) {
pk-button#btnSession { animation: none; }
}
[[2-big-flags-on-the-first-screen]] == 2. Big flags, on the first screen
This is the mistake we most want you not to repeat. In our sample, language is a small round button in the top-left corner opening a popover. On the kiosks we run, changing language turned out to be one of the most used controls on the tablet — yet it remains the smallest target on the glass, unlabelled, in the corner, and easily missed.
Display your supported languages on the idle screen as large flag tiles next to Start. Visitors pick a language, then press play. This natural order means nobody has to discover a hidden corner.
sends |
|
reads |
|
goes quiet while |
|
Which languages exist is a host setting, not a tablet one — AvailableLocales on the PromethistLink
component. The tablet merely draws whatever arrives in locales. Adding Polish requires just one
Unreal property plus a flag image. In the sample, ICONS in tablet.ts maps a code to its flag,
while LANGUAGE_NAMES provides a name for screen readers. The setUpLanguagePicker() function in
main.ts builds the tiles; read it before writing your own.
Two crucial things the sample gets right:
-
Changing language restarts the conversation, so the tiles must lock while a session change is in flight. This is what
sessionBusyis for. -
The tablet’s own labels follow the chosen language, so picking a flag re-translates the entire screen, not just the avatar.
[[3-call-assistance—only-if-somebody-will-answer]] == 3. Call assistance — only if somebody will answer
This is worth having when on-site staff can receive notifications. Otherwise, omit it entirely: a button summoning an absent human is worse than no button.
The wait is the interesting part, and the protocol handles it for free:
On Tablet Command ("assistance")
-> Set Tablet State Bool ("assistancePending", true)
-> ... your HTTP call: a Teams incoming webhook, a pager, a bell in the back office ...
-> Set Tablet State Bool ("assistancePending", false)
While that key is true, the button lights up, spins where its icon was, says "Calling assistance…",
and refuses a second press — on every tablet in the room. This works because it is published state,
not a private reply. A tablet reloading mid-wait comes back still showing it.
Two obligations:
-
Publish
falseonBeginPlaytoo. If a level travel destroys your graph mid-wait, nothing is left to clear the key. The tablet will hold a spinner forever over an action nobody is doing. -
Do not grey it out on
agentUnready. That key means "there is no conversation running right now" — true for an idle kiosk, and true the moment anything goes wrong. Locking the button on this key disables it for most of the day, including when somebody actually needs a human. It is the right key for buttons acting on a conversation, like Pause or Restart, but the wrong key for this one.
[[4-keep-the-hidden-admin-menu]] == 4. Keep the hidden admin menu
This is the one part of the sample we would not change. A three-second press on the logo — lacking visible affordance, so visitors cannot stumble into it — opens an admin menu with controls for whoever manages the kiosk:
Volume |
|
Microphone mute |
|
Manual blackout |
Takes the kiosk out of service. |
Worth knowing: the same press works while the tablet is fogged, hitting the exact same spot on the glass. An absent host must not lock an operator out of their own controls. Manual blackout lives inside the admin menu, so an admin menu closing itself when fogged would trap the person trying to lift the lockout they just applied.
If building your own admin menu, copy the two gesture rules from gestures.ts rather than reinventing
them (next section).
What we would leave out
Nothing in the sample is wrong; everything exists because some installation requested it. However, start from zero and add:
| Sample control | Add it when |
|---|---|
Pause / Resume |
staff need to hold a conversation mid-sentence — a tour group arriving, say |
Subtitles |
your project actually draws subtitles on the big screen |
Restart |
conversations get stuck often enough that somebody wants a fast reset |
Joke ("say something") |
you are demoing, or want canned phrases. It is |
The five-star rating |
you want feedback from visitors — see Asking the visitor something below |