Embedding a distil study
Add one line to the page and a chat bubble appears in a corner. A person clicks
it, talks with distil about your brief, and you read what they said at
distil.cx. The snippet never changes once pasted; the look (colour, logo, font)
lives on the study.
<script src="https://distil.cx/embed.js"
data-distil="SHARE_TOKEN"
data-launcher
data-pid="USER_ID"
async></script>
SHARE_TOKEN is the study’s share link token — the last part of its link.
Nothing is drawn, and nothing on your page shifts, until distil confirms the
study is open for this person.
Attributes
| attribute |
meaning |
data-distil |
required; the share token. Without data-launcher the conversation is framed inline where the tag sits (size it with data-width / data-height, default 480px × 640px). |
data-launcher |
draw a corner button that opens the conversation in a panel (a full-screen sheet at 600px wide or narrower, or 560px high or shorter). |
data-pid |
your opaque user id, forwarded to the study so you can join responses to your users. |
data-context-<key> |
what the person just did, e.g. data-context-level="Year 9". |
data-position |
bottom-right (default) or bottom-left; the panel opens from the same side. |
data-offset-x, data-offset-y |
distance in px from the side and from the bottom; whole numbers, default 20, clamped to 0–400. Use them to clear your own help bubble. |
data-hidden |
with data-launcher: load without drawing the bubble, so you open it yourself with Distil.open(). |
pid and context
The pid is stored only if it is at most 64 characters of A-Za-z0-9_-;
anything else is dropped. Send an internal id — never a name or an email, and
the charset rejects both. It identifies your user; it does not prove who they are.
Each data-context-<key> is kept if the key (hyphens become _) is 1–24 of
a-z0-9_ — at most 6 keys — and the value is 1–40 characters of letters, digits,
space and _-,./&+. Anything else is dropped. Both appear beside each response
in distil and in distil conversations --json. The interviewer does not read them.
JavaScript API
window.Distil exists as soon as the script runs, even when nothing is drawn.
Distil.open(); // open the panel
Distil.close(); // close it
Distil.dismiss(); // remove the launcher and remember that, as the visitor's dismiss button does
open() works after a dismissal, because you asked explicitly. It does nothing
when the study is not open or this pid has finished, and it waits for the check
if that is still pending.
Events fire on document: distil:ready (detail.live is true or false),
distil:open, distil:close, distil:dismiss, distil:done.
document.addEventListener("distil:done", () => analytics.track("study_done"));
When the launcher is not shown
- Dismissed. Your page called
Distil.dismiss(). We store
distil:dismissed:<token>:<pid> in your site’s sessionStorage, so the bubble
returns on the next visit.
- Done. The visitor finished the conversation. The panel stays open on the
thank-you screen and the launcher goes when they close it; we store
distil:done:<token>:<pid>.
- Closed. The study is at its response cap or past its end date, or this pid
already finished.
- Unreachable. distil.cx did not answer within about 8 seconds, or errored.
In each case your page is left exactly as it was. If localStorage is blocked,
the launcher still works but forgets dismissals between visits. The launcher
never throws on your page. Your browser’s network log may show a 404 for the
check when a study is closed or finished; that is the signal, not a fault.
Accessibility
Escape closes the panel. Focus moves into the conversation on open and back to
the bubble on close. The bubble announces its open state; the mobile sheet is a
modal dialog and respects safe-area insets.
Content Security Policy
If you set a CSP, allow:
script-src https://distil.cx;
img-src https://api.distil.cx;
frame-src https://distil.cx;
The launcher loads its icon and its open/closed check as images from
api.distil.cx. The conversation is a frame of https://distil.cx/e/<token>;
it uses the microphone for voice answers, which the frame requests through its
allow attribute (microphone; clipboard-write) — your own Permissions-Policy
must not block the microphone for it. distil makes no cross-origin requests from
your page, so you need no CORS configuration on either side.