stimFormat
Experiments Surveys & forms On form rows it controls whether images preload next to the question; it does not make a row a survey question (that is the type column).
stimFormat declares what kind of stimuli the trial’s stim columns hold. It determines how the trial’s stimuli are displayed (mapping) and, for media trials, doubles as the literal file extension appended to stimulus basenames. The special value list switches the row into stimulus-list expansion mode.
Syntax
Section titled “Syntax”Put the file extension of your stimuli in the cell, leading dot included: .png, .mp3, .mp4.
For stimuli that are not files, use a keyword instead: word for text shown on screen, survey, html for a custom HTML app, or list to draw the trial’s stimuli from an uploaded stimulus list.
The cell holds a single value and letter case does not matter.
Options
Section titled “Options”Stimulus type mapping:
| Value | Trial type | Notes |
|---|---|---|
.bmp .gif .jpeg .jpg .pdf .png .webp | image | .tiff also works, but is deprecated |
word | word | Stim cells rendered as text/HTML |
.aac .flac .m4a .mp3 .ogg .opus .wav | audio | .aiff .wma deprecated |
.mp4 .webm | video | .ogv .wmv .avi .flv .mkv .mov deprecated |
survey | survey | |
html | app | Loads stim/stim1; see “Custom HTML trials” below for the in-app testableSDK API |
list | derived | Type computed from the first picked stimulus’s extension; extensionless or unknown-extension first stim becomes word unless type is instructions |
anything else, incl. an empty cell, image, audio, video, form | instructions | Default case |
Note there is no form stimFormat; form trials are driven by the type column.
Defaults & missing values
Section titled “Defaults & missing values”Leave the cell empty, or leave the column out, and the row becomes an instructions trial.
Works with
Section titled “Works with”stim,stimListandstimN:stimFormatdecides whether the values in those cells are filenames (media), literal text (word), stimulus-list names (list), or an app reference (html).- On image, audio and video trials you write the filename without its extension and
stimFormatsupplies it. If a cell already ends in exactly that extension, it is not appended a second time. - On
listrows the trial’s type and extension come from the first stimulus picked from the list, and the media that gets preloaded comes from the picked rows (stim_actual) rather than the stim columns. - A single row with
stimFormat: list(any case, surrounding spaces ignored) switches the whole trial file into list expansion, and every generated row keepsstimFormat: list. Two things about the column itself can stop that happening for the whole file: see Tips & gotchas. - In multiplayer arenas,
stimFormatis part of the trial information shared between players. stimScreens(screen grouping) is not supported on audio or video trials and is rejected when you save. On rows made dynamic bystimFormat: list, or by#list#markers instimorstimList,stimScreensaccepts only a single broadcast count.
Examples
Section titled “Examples”Image trial, basenames in stim columns, extension from stimFormat:
| type | stimFormat | stim1 | stim2 | presTime | keyboard | key |
|---|---|---|---|---|---|---|
| test | .png | circle | square | 500 | 1 2 | 1 |
type,stimFormat,stim1,stim2,presTime,keyboard,key test,.png,circle,square,500,1 2,1
Word trial:
| type | stimFormat | stim1 | keyboard | key |
|---|---|---|---|---|
| test | word | HELLO | h j | h |
type,stimFormat,stim1,keyboard,key test,word,HELLO,h j,h
List trial (type derived from picked stimuli’s extensions):
| type | stimFormat | stim | listOptions |
|---|---|---|---|
| test | list | sounds | random |
type,stimFormat,stim,listOptions test,list,sounds,random
Custom HTML trials (stimFormat=html): loading and the testableSDK
Section titled “Custom HTML trials (stimFormat=html): loading and the testableSDK”stimFormat: html turns the trial into an “app” trial: the value of stim (preferred) or stim1 (fallback) is loaded into an iframe after the trial’s ITI. The trial then waits indefinitely for the app to save a response; keyboard/button response columns do not apply.
When to resort to a custom HTML trial. Reach for stimFormat: html only when no built-in trial expresses the interaction: drag-and-drop, canvas/games, gesture or free-drawing input, multi-step logic inside a single trial, rich third-party widgets, or embedding an externally hosted web app. Prefer built-ins first: keyboard/button/click responses for RT tasks, responseType widgets (slider, box, dropdown, likert, rank, stars) and type=form for surveys, stimFormat media types for images/audio/video. A custom HTML trial trades away Testable’s automatic handling: its RT is coarser (see below), key scoring, responseWindow, presTime and feedback columns do not apply, and the app itself must call saveHtmlTrialResponse or the trial never advances. The app’s files (HTML plus any CSS/JS/media) must be uploaded to the project’s Stimuli section beforehand: over MCP, use put_stimulus to upload them (text content for html/js/css, base64 for binaries), list_stimuli to check what exists, and delete_stimulus to remove files.
Loading and URL resolution
Section titled “Loading and URL resolution”- The stim cell first goes through variable replacement, so
%response%,%rt%,%correct%,%responseCode%and the full variable syntax are substituted. Pattern:index.html?a=%response%passes the previous trial’s response as a query parameter, readable in the app vianew URLSearchParams(window.location.search).get('a'). - Predefined app names
cubefieldandlangfusemap to built-in apps. - Values starting with
httpare loaded as-is (externally hosted apps are allowed). - Anything else is a relative path into the project’s stimuli folder:
.htmlis AUTO-APPENDED when the value does not already contain.html, and leading../sequences are stripped. Sostim: my_trialandstim: my_trial.htmlboth work when the experiment runs (the editor’s validation is stricter; see thestimpage). - A cache-busting
ts=<timestamp>query parameter is always appended, including for external URLs. - Uploaded HTML/CSS/JS files and media stimuli all live in the same stimuli folder, so they can reference each other with bare relative paths (
styles.css,script.js,image1.png).
Saving the response: parent.testableSDK.saveHtmlTrialResponse(response[, data])
Section titled “Saving the response: parent.testableSDK.saveHtmlTrialResponse(response[, data])”Calling it records response as the trial’s response, tears down the iframe, and advances to the next trial. The response string is trimmed before it is saved.
The optional data object saves custom variables:
- Each key becomes its OWN COLUMN in the exported results CSV, with the value placed on the row of the trial that saved it and blank on all other rows.
- Keep values primitive (string/number). Nothing checks that the object is flat: a nested object is stored and serialized into the CSV as
[object Object]. - The same object is also reported to the attrition/consort tracking while the trial saves; an oversized object (longer than 50000 characters once serialized) is silently left out of that report.
RT: the recorded RT is measured from the moment the app trial is SCHEDULED, before the ITI elapses and before the iframe loads. So the saved RT includes the ITI wait plus the app’s load time, not just time-in-app.
Cross-origin apps: postMessage bridge
Section titled “Cross-origin apps: postMessage bridge”Externally hosted apps cannot call parent.testableSDK directly (cross-origin). Instead they post a message to the parent window:
window.parent.postMessage({ testable_app_result: { response: "Button clicked", custom_variables: { score: 85 } }}, "*");The runner listens for message events and forwards testable_app_result.response / .custom_variables to saveHtmlTrialResponse.
Reading data from inside the app
Section titled “Reading data from inside the app”parent.testableSDK.variables.name— latest saved custom variable, orundefinedif never saved.parent.testableSDK.variables.getValue(name, ref)reads at a history point:ref−1 (default) = previous trial, −2 = one before that, a number ≥ 2 = absolute spreadsheet row (data rows start at 2; header is row 1), or alabelcolumn value. The lookup is CARRY-FORWARD: it returns the latest value saved at or before the referenced trial, so a trial that did not itself save the variable still yields the preceding value;undefinedonly when no earlier trial ever saved it.parent.testableSDK.allocatedSubjectGroups— array of the participant’s picked subject groups aslabel:pickstrings; picks made under the auto-added default label appear as the bare pick, with no label prefix.parent.testableSDK.columnForTrial(columnName, trialIndex)— standard-column value from any trial row;trialIndexis the 0-based data-row index, and an empty cell comes back asundefined. Unknown or custom column names throw a TypeError.parent.testableSDK.trial.columnName— current-trial proxy. It does NOT work for standard columns:trial.stim1and its siblings always returnundefined, and only CUSTOM columns come back. Useparent.testableSDK.columnForTrial(name, window.parent.trialRow)or a custom column as the workaround.
Minimal example
Section titled “Minimal example”| type | stimFormat | stim |
|---|---|---|
| test | html | my_trial |
type,stimFormat,stim test,html,my_trial
my_trial.html (uploaded to Stimuli alongside any css/js/images it references):
<button id="go">Click me</button><script> document.getElementById('go').addEventListener('click', function () { parent.testableSDK.saveHtmlTrialResponse('Button clicked', { time_taken: 15, confidence: 'high' }); });</script>Tips & gotchas
Section titled “Tips & gotchas”- HTML trials: the
testableSDK.trialproxy returnsundefinedfor every standard column, so read standard columns withcolumnForTrialinstead. Recorded RT for app trials includes the ITI and the app’s load time. Nested objects insaveHtmlTrialResponse’s data parameter export as[object Object]. - Only an extension identical to
stimFormatis stripped from a stim cell, so a cell that carries a DIFFERENT extension ends up with both (a.pngon a.jpgtrial becomesa.png.jpg). - Stimulus lists recognise a narrower set of extensions than ordinary trials do. If the first stimulus picked from a list ends in something outside
.jpg,.jpeg,.png,.gif,.bmp,.pdf,.mp3or.mp4(for example.webp,.wavor.webm), the trial is treated as awordtrial, and only.mp3and.mp4files from a list are preloaded. Stick to that narrower set inside stimulus lists.
Common combinations
Section titled “Common combinations”stimFormat appears in these worked recipes:
- Replayed random order: Shuffle a block once, then replay that exact order in a later block so both passes share identical order effects.
- Ambient audio across trials: Start a looping background track on one trial and cut it at a later trial, leaving the rest of the block silent.
- Between-subject conditions: Give each participant one of two stimulus sets, while shared screens run for everyone.
- Nested block shuffle: Randomise the order of whole task sections and of the mini-blocks inside each section, keeping every block’s trials together.
- Respond after the clip ends: Play an audio clip and only accept the keypress once playback has finished, with RT measured from the end of the clip.