Skip to documentation
Workframe guideOverview

Turn a vignette into an experience

Workframe is a browser tool for building short, choice-driven simulations that run inside a survey. Instead of reading a paragraph describing a situation, your participant lives through a brief experience — characters speak, things happen, and they make choices that shape what unfolds. Every choice is recorded. A study can publish several independent experiences into different Qualtrics steps while keeping their data connected.

You don’t need to choose between a “simple” and “advanced” experiment. Outline, Flow, and Timeline are lossless views of the same study. Start in the representation that fits the task and switch whenever you need a different level of detail. Delivery is configured when you publish, not as a fourth editing view.

What you can build

  • A paced scene with a background, characters, dialogue, and timed questions.
  • Any number of experimental conditions (2 to N) — the tool never assumes a fixed count.
  • Branches where a participant’s choice slightly alters how characters respond.
  • Clean, per-participant data linked back to Qualtrics and Prolific IDs.

How do I…?

One answer per thing you came here to do, each written as the sequence of buttons you actually press. Pick a task; the guide below explains the ideas behind it.

Build a scene

Branch and vary

With your AI

Run and ship

How do I try my study before anyone sees it?

Open the studyParticipant previewPlay
  1. Click Participant preview in the top bar.

    It plays the open experience the way a participant gets it — timed dialogue, pauses, questions and all.

  2. Answer as a participant would.

    Routing, conditional clips and branch destinations all run for real, so a wrong turn here is a wrong turn in the study.

  3. To rehearse the real thing, publish and open your Qualtrics survey preview.

    That exercises the embed itself: the iframe, the embedded-data fields and the hidden Next button.

  4. Look at the run in Results.

    Editor previews and Qualtrics survey previews are both marked as test data and hidden from the default view, so rehearsing never pollutes your analysis. Switch the view to Test or All to see them.

Draft a study with AI

Already have your scenario written up? Turn it into a working study in seconds. Bring the document to the AI you already use, and Workframe assembles the result into a real project you refine on the timeline — instead of laying out every scene by hand.

How it works

  1. Open the importer

    From the projects dashboard, the project menu, or the new-project dialog, choose Import from document.

  2. Prepare the file with your AI

    Copy the built-in prompt, paste it into your AI assistant together with your script, and it hands back a study.json file. If it shows the JSON as text instead, paste that into the importer.

  3. Drop it in and review

    You get a summary — experiences, scenes, dialogue lines, choices, branch points and events — so you can confirm the study reads correctly before anything is saved.

  4. Open in the builder

    It becomes a normal project. Refine, preview and publish exactly as you would any study built by hand.

What it sets up for you

  • Scenes in order, with your narration and dialogue reproduced word for word.
  • The people who speak, staged on a fitting background as figures you can recast.
  • Cosmetic choices that rejoin, and consequential choices whose later lines react to the answer.
  • Named events, ready to log for analysis.

Your script stays with you: the file is read in your browser, and only the compiled study is saved when you open it. The AI organises your material — your wording is never rewritten or invented — and you review every scene before publishing.

Core concepts

Nine ideas underpin everything else. Learn these and the rest is detail.

StudyThe complete research unit, including shared variables, assets, experiences, and Qualtrics placements.
ExperienceOne independently launchable Workframe flow. A study can contain several experiences that never run sequentially.
PlacementWhere an experience appears in Qualtrics. Qualtrics owns the order between placements.
SceneA compound block with a stage and duration. It retains its internal timeline in every representation.
TimelineThe scene’s clock. You place clips along it to control when things appear.
TrackA horizontal lane for one kind of content — environment, characters, dialogue, interactions, or signals.
ClipOne timed element on a track: a background, an actor, a line, a question, a signal.
ConditionOne version of the manipulation. Which condition a participant gets is decided outside the tool and passed in.
VariableA recorded value — a participant’s answer, or something you set — that becomes a data column and can steer content.
SignalA named, invisible marker fired at a moment, used to gate later content and mark treatment events.

One study, several representations

The view switcher changes how the same canonical experiment is shown. It never converts, duplicates, or flattens your work.

Outline

Read and assemble the participant journey using ready blocks such as Present, Ask, Decide, Record, and Wait. Questions show their stored response variable, routes, and conditional items where they occur.

Flow

Inspect a wrapped participant map without a horizontal strip. Select a block to see its incoming and outgoing routes, path health, and the logic that runs inside it.

Timeline

Fine-tune timing, overlap, staging, motion, media, and event capture. The left rail separates the scene navigator from reusable library material.

Getting around the Timeline: choose Scenes in the left rail to navigate and organize the current experience. Choose Library for reusable Assets or shared Data (variables). Your work autosaves as you edit, and the editor keeps an undo history (Ctrl/Cmd+Z).

The five tracks

Every clip lives on one of five tracks. Click a track to see how to use it.

Clips & the stage

A clip is a timed element. You give it a start and a duration on the timeline, then position it on the stage — the fixed 1920×1080 artboard your participant sees. Drag a clip to move it, or drag a corner to scale it; arrow keys nudge by 1% (Shift for 5%).

What every clip carries

  • Placement: position (X/Y as a percentage), scale, rotation, and opacity.
  • Timing: when it starts and how long it lasts (half a second minimum).
  • Content: the asset, dialogue text and speaker, or question, depending on the track.

The stage is always 1920×1080. You can zoom the editor view to fit or up to 200% for fine work — that only changes your view, not what participants see.

Motion & timing

Clips can animate in, hold, and animate out. Use the Simple mode for common presets, or Fine-tune for full control with two keyframes — a start pose and an end pose that the clip interpolates between.

SettingOptions
Entryfade · slide-left · slide-right · rise · pop · zoom
Exitfade · slide-left · slide-right · sink · shrink · zoom
Loopfloat · pulse · sway (a gentle idle motion)
Easingease-out · ease-in-out · linear
Transition0.05–5 seconds

For an advanced non-verbal manipulation (a character shifting to make space, a nod), prefer a motion keyframe plus a short action cue over a whole new image. Add a separate pose image only when the visual state itself is what you’re manipulating.

Questions & responses

An interaction clip asks the participant something. Pick the response format:

Single choice

One answer from a list of options (minimum two). Each option records a stable value and can optionally route to another scene.

How answering behaves

  • Pause for answer (default): the timeline freezes until the participant responds, then resumes. Choose Keep playing if the scene should continue regardless.
  • Give the question a clear response variable. It becomes the column you can reuse in later rules, dialogue templates, and analysis; Outline shows this directly on the question block.
  • Each option also has a stable option value — a clean label for your data that stays fixed even if you reword the button.

Branching & routing

There are two ways one moment leads to the next:

  • Scene route: when a scene ends it continues to the next scene in order, jumps to a specific scene, or ends the study.
  • Choice route: an individual answer can override that and jump immediately to another scene. This is how a choice sends someone down a different path.

To make a choice slightly alter responses without a whole new path, leave the routes alone and instead gate a couple of minimally-different dialogue clips with a participant-choice rule — so the character’s next line reflects what was chosen. Reuse the actual answer in the line with templating (see below).

Use Flow to inspect the complete route. It wraps long paths into a map, keeps branch routes visible, and shows path health for the selected block. Set an answer’s destination in its Outline question block; set a scene’s automatic continuation in the Timeline. Then return to Flow to check the participant journey.

Conditions & rules

This is the manipulation engine. Any clip can be shown or hidden by a rule. A clip with no rule always plays. The tool supports any number of conditions — it reads whatever condition value is passed in and shows the matching content.

RuleWhen to use itYou provide
condition
Study URL condition equals…
Show this content only to one condition. Which condition a participant is in is decided upstream (Qualtrics/Prolific) and passed in as the CONDITION value.a condition value, e.g. Transparent AI
assignment
Assigned group equals…
A second, independent grouping alongside condition (passed as ASSIGNMENT). Useful for a 2×2 or a nested factor.a group value, e.g. Variant B
participant-choice
A recorded variable equals…
Branch on something the participant already did — show a follow-up only to people who chose a particular answer earlier.a variable + the value it must equal
prior-event
An earlier signal fired…
Show content only after a specific moment happened earlier in the run (a signal was emitted).a signal key
chance
Random draw is within…
Randomize within a single run — a coin-flip shown to some participants. Sampled once per session, not every frame.a percentage, 0–100

Rules decide who sees something or after what — they never pause the timeline. To hold the scene, use a “Pause for answer” question instead. In Outline, a conditional item is marked beside the exact line, question, or event it controls; in Flow, select the block to read its logic in plain language.

Building N conditions

Give the content that differs between conditions a condition rule set to that condition’s value. Content shared by everyone gets no rule. One project can hold every condition at once; the participant only ever sees the one their assigned condition matches.

Variables & templates

A variable is a named value. Some are created automatically from your questions; you can also make manual ones and set them at a chosen moment. Variables become columns in your results and can be woven back into the story.

Reuse an answer in dialogue

Wrap a variable key in double braces and it’s replaced with the recorded value at play time:

Great — let's plan for {{meeting_time}}, then.

If the participant earlier chose “3pm”, the character says “Great — let’s plan for 3pm, then.” This is what makes a choice feel like it mattered.

Signals

A signal is an invisible, named event you fire when a clip ends — for example ICB_3_make_space. Participants never see it. Signals do two jobs:

  • Gate later content with a prior-event rule, so something only appears once the signal has fired.
  • Mark treatment moments so each manipulation is recorded independently and can be removed or swapped per condition.

Assets & media

Assets are the reusable pieces you place on the stage: characters, backgrounds, and interaction placeholders. The tool ships with a starter library of characters, expression variants, and workplace backgrounds so you can build immediately.

  • In Timeline, open Library → Assets to browse or add reusable media. Open Library → Data to manage variables; Scenes remains a separate navigation mode.
  • Add your own by uploading PNG, JPEG, or WebP — up to 10 MB each, 250 MB per project, and 500 MB or 1,000 files per account.
  • Rename an asset to a study-specific role (e.g. reuse one portrait as “the new colleague”); the name follows through to the clips that use it.

The 4 MB cap keeps the editor responsive. For heavier production media, plan to move to hosted storage later.

Publish to Qualtrics

Each placement becomes one Qualtrics question. Qualtrics owns the survey sequence and randomization; Workframe launches the selected experience and returns placement-scoped completion and response fields. Publish generates a separate snippet for every placement.

One prerequisite: the player must live at a publicly reachable URL a participant’s browser can load. A local or preview address won’t work for real respondents — deploy first, then use that host below.

1. Player URL

https://your-workframe-host/study?embed=1&VERSION_ID=version-from-publish&CONDITION=${e://Field/simulation_condition}&ASSIGNMENT=${e://Field/simulation_assignment}&Q_CHL=${e://Field/Q_CHL}&QUALTRICS_RESPONSE_ID=${e://Field/ResponseID}&PROLIFIC_PID=${e://Field/PROLIFIC_PID}&STUDY_ID=${e://Field/STUDY_ID}&SESSION_ID=${e://Field/SESSION_ID}

2. Embed (paste into a Text/Graphic question)

<iframe id="workframe-simulation" src="https://your-workframe-host/study?embed=1&VERSION_ID=version-from-publish&CONDITION=${e://Field/simulation_condition}&ASSIGNMENT=${e://Field/simulation_assignment}&Q_CHL=${e://Field/Q_CHL}&QUALTRICS_RESPONSE_ID=${e://Field/ResponseID}&PROLIFIC_PID=${e://Field/PROLIFIC_PID}&STUDY_ID=${e://Field/STUDY_ID}&SESSION_ID=${e://Field/SESSION_ID}" width="100%" style="border:0;border-radius:12px;aspect-ratio:16/9" allow="autoplay"></iframe>

3. Question JavaScript

Qualtrics.SurveyEngine.addOnload(function () {
  var q = this;
  q.hideNextButton();
  var frame = document.getElementById("workframe-simulation");
  var sideMargin = 24;
  var bottomMargin = 24;
  var maxWidth = 1600;
  var trusted = new URL("https://your-workframe-host").origin;
  function disableQuestionOverflowFade(questionRoot, player) {
    if (!questionRoot || !player) return;
    questionRoot.classList.add("workframe-embed-question");
    var overflowFixId = "workframe-qualtrics-overflow-fix";
    var overflowFix = document.getElementById(overflowFixId);
    if (!overflowFix) {
      overflowFix = document.createElement("style");
      overflowFix.id = overflowFixId;
      document.head.appendChild(overflowFix);
    }
    var overflowFixCss =
      '.workframe-embed-question .overflow-container::before,' +
      '.workframe-embed-question .overflow-container::after,' +
      '.workframe-embed-question [class*="overflow-container"]::before,' +
      '.workframe-embed-question [class*="overflow-container"]::after,' +
      '.workframe-embed-question [class*="OverflowContainer"]::before,' +
      '.workframe-embed-question [class*="OverflowContainer"]::after{' +
      'content:none!important;display:none!important;background:none!important;' +
      'box-shadow:none!important;mask-image:none!important;-webkit-mask-image:none!important}';
    if (overflowFix.textContent !== overflowFixCss) overflowFix.textContent = overflowFixCss;
    var scrollShadows = questionRoot.querySelectorAll(
      '.overflow-container .shadow, [class*="overflow-container"] [class*="shadow"], [class*="OverflowContainer"] [class*="Shadow"]'
    );
    for (var shadowIndex = 0; shadowIndex < scrollShadows.length; shadowIndex += 1) {
      var scrollShadow = scrollShadows[shadowIndex];
      if (scrollShadow.contains(player)) {
        scrollShadow.style.setProperty("background-image", "none", "important");
        scrollShadow.style.setProperty("box-shadow", "none", "important");
        scrollShadow.style.setProperty("mask-image", "none", "important");
        scrollShadow.style.setProperty("-webkit-mask-image", "none", "important");
      } else {
        scrollShadow.style.setProperty("display", "none", "important");
      }
    }
  }
  function fitFrame() {
    frame = document.getElementById("workframe-simulation");
    if (!frame) return;
    var questionRoot = frame.closest("section.question") || frame.parentElement; disableQuestionOverflowFade(questionRoot, frame);
    var ancestor = frame.parentElement;
    while (ancestor && ancestor !== document.body) {
      ancestor.style.setProperty("overflow", "visible", "important");
      ancestor.style.setProperty("max-width", "none", "important");
      ancestor = ancestor.parentElement;
    }
    frame.style.setProperty("position", "relative", "important");
    frame.style.setProperty("display", "block", "important");
    frame.style.setProperty("transform", "none", "important");
    frame.style.setProperty("margin", "0", "important");
    frame.style.setProperty("left", "0", "important");
    frame.style.setProperty("max-width", "none", "important");

    var initialRect = frame.getBoundingClientRect();
    var viewportWidth = document.documentElement.clientWidth || window.innerWidth;
    var viewportHeight = window.innerHeight;
    var maxByWidth = viewportWidth - sideMargin * 2;
    var availableHeight = viewportHeight - initialRect.top - bottomMargin;
    var maxByHeight = availableHeight * (16 / 9);
    var width = Math.max(320, Math.min(maxByWidth, maxByHeight, maxWidth));
    var height = width * (9 / 16);

    frame.style.setProperty("width", width + "px", "important");
    frame.style.setProperty("height", height + "px", "important");
    frame.style.setProperty("aspect-ratio", "16 / 9", "important");
    var desiredLeft = (viewportWidth - width) / 2;
    var currentRect = frame.getBoundingClientRect();
    frame.style.setProperty("left", (desiredLeft - currentRect.left) + "px", "important");
  }
  fitFrame();
  window.requestAnimationFrame(fitFrame);
  window.addEventListener("resize", fitFrame, false);
  var frameObserver = new MutationObserver(function () {
    if (document.getElementById("workframe-simulation")) fitFrame();
  });
  frameObserver.observe(document.documentElement, { childList: true, subtree: true });
  window.addEventListener("message", function (event) {
    if (event.origin !== trusted) return;
    if (frame && event.source !== frame.contentWindow) return;
    var d = event.data || {};
    if (d.type === "workframe:signal") {
      var signalKey = String(d.signal || "").replace(/[^A-Za-z0-9_]/g, "_");
      if (signalKey) Qualtrics.SurveyEngine.setEmbeddedData("signal_" + signalKey, "1");
      window.dispatchEvent(new CustomEvent("workframe:signal", { detail: d }));
      if (d.signal === "qualtrics_continue") q.showNextButton();
      return;
    }
    if (d.type !== "workframe:simulation-complete") return;
    Qualtrics.SurveyEngine.setEmbeddedData("simulation_complete", "1");
    Qualtrics.SurveyEngine.setEmbeddedData("simulation_session_id", d.sessionId || "");
    Qualtrics.SurveyEngine.setEmbeddedData("simulation_choices", JSON.stringify(d.responses || {}));
    q.showNextButton();
  });
});

The ${e://Field/…} pieces are Qualtrics piped text — leave them as-is; Qualtrics fills them in per respondent.

Set-up steps in Qualtrics

  1. Create the embedded-data fields

    In Survey Flow, add simulation_condition, simulation_assignment, simulation_complete, simulation_session_id, and simulation_choices.

  2. Randomize the condition

    Use a Randomizer (or branch logic) to set simulation_condition to one of your condition values, as many as your design needs.

  3. Add a question and paste the iframe

    Drop the HTML snippet into a Text/Graphic question so the simulation renders inline.

  4. Paste the question JavaScript

    It stores named signals as embedded data and hides the Next button until completion. A timeline signal named qualtrics_continue can reveal it earlier.

Prolific is optional. The Qualtrics ResponseID is the primary survey join key; when recruitment comes from Prolific, PROLIFIC_PID, STUDY_ID, and SESSION_ID also pass through for reconciliation.

Reading your data

The Results workspace provides an exportable participant summary and an exportable detailed audit trail. Both are always recorded and kept for every version you publish — there is nothing to switch on, and nothing is ever discarded. Either view exports as CSV or JSON.

Participants view

One row per participant. Alongside their IDs and condition you get a column for every response variable and every signal that fired, plus timing:

qualtricsResponseIdprolificPidsessionIdconditionassignmentstartedAtcompletedAtcompleteddurationMsscenesCompleted

Which should I use? Use the participant summary for analysis: it is one clean row per participant, with the variables, treatment signals, and completion details your study was designed to measure. Detailed audit events are usually unnecessary for analysis because they repeat that information as a sequence of implementation-level steps. Reach for them when you need to diagnose an unexpected route, verify a complex branch, or study the participant’s process itself — they are always there.

Raw events view

Recorded and kept for every version. A compact audit trail in order — session start, responses, routing signals, scene transitions, and completion — for checking the path a participant took, without recording routine playback mechanics.

Runs from the editor preview — and any run opened through a Qualtrics survey preview (the Q_CHL field the snippet pipes in) — are flagged as test data and hidden by default. Switch the results view to Test or All to see them, and keep them out of your real analysis.

Glossary

AssignmentA second grouping factor passed alongside condition, for nested or 2×2 designs.
Choice route / destinationA scene an individual answer jumps to, overriding the normal scene order.
Embedded dataQualtrics fields the simulation reads from and writes back to.
Option valueA stable, analysis-friendly label for an answer, independent of the visible button text.
Playback modeWhether a question pauses the timeline (“Pause for answer”) or lets it keep playing.
SessionOne run of the simulation, with a unique ID. A reload starts a fresh session so streams never merge.