Skip to main content

@slopit/adapter-jatos API Reference

Integration adapter for JATOS (Just Another Tool for Online Studies). Provides session recording with automatic integration into JATOS's data storage and multi-component study support.

Installation​

pnpm add @slopit/adapter-jatos

When to Use This Package​

Use @slopit/adapter-jatos when:

  • Running experiments on a JATOS server
  • Building multi-component studies
  • Using JATOS for experiment hosting and data collection

Quick Start​

import { SlopitRecorder } from "@slopit/adapter-jatos";

jatos.onLoad(function() {
const recorder = new SlopitRecorder();

// run trials
recorder.beginTrial("trial_1", textarea);
// ... participant completes trial ...
recorder.finishTrial(response);

// submit and end
await recorder.submitToJatos();
jatos.endStudy();
});

SlopitRecorder / JatosRecorder​

Session recorder for JATOS experiments. Automatically detects JATOS worker and study IDs and integrates with JATOS's result data APIs.

Both SlopitRecorder and JatosRecorder refer to the same class.

Constructor​

new SlopitRecorder(config?: JatosRecorderConfig)
new JatosRecorder(config?: JatosRecorderConfig)

The recorder automatically extracts participantId from jatos.workerId and studyId from jatos.studyId if not provided in the config.

JatosRecorderConfig​

PropertyTypeDefaultDescription
participantIdstringjatos.workerIdParticipant identifier (auto-detected)
studyIdstringjatos.studyIdStudy identifier (auto-detected)
metadataRecord<string, unknown>-Session-level metadata
behavioralBehavioralCaptureConfig-Default behavioral capture settings
resultKeystring"slopit"Key used for slopit data in JATOS results
const recorder = new SlopitRecorder({
resultKey: "behavioral_data",
metadata: {
condition: "experimental",
batch: "batch_1",
},
});

beginTrial(trialId, element?, trialConfig?)​

Starts capturing behavioral data for a trial.

beginTrial(
trialId: string,
element?: HTMLElement,
trialConfig?: Partial<TrialConfig>
): void
const textarea = document.querySelector("textarea");
recorder.beginTrial("trial_1", textarea, {
trialType: "free-response",
stimulus: {
type: "text",
content: "Describe your experience.",
},
});

startTrialWithSelector(trialId, selector, container?)​

Starts a trial with automatic element discovery. Uses a MutationObserver to wait for the element to appear in the DOM.

startTrialWithSelector(
trialId: string,
selector: string,
container?: HTMLElement | Document
): void
recorder.startTrialWithSelector(
"trial_1",
"textarea.response-input",
document.getElementById("trial-container")
);

finishTrial(response?)​

Ends the current trial and returns captured data.

finishTrial(response?: string | ResponseInfo): CompletedTrial
// with text string
const trial = recorder.finishTrial("The participant's response.");

// with ResponseInfo object
const trial = recorder.finishTrial({
type: "text",
value: "Response text",
characterCount: 13,
wordCount: 2,
});

submitToJatos()​

Submits session data to JATOS. Call this at the end of the component or study.

async submitToJatos(): Promise<void>
await recorder.submitToJatos();
jatos.endStudy();

appendToJatos()​

Appends session data to JATOS result. Use this for incremental data submission during long experiments.

async appendToJatos(): Promise<void>
// after each block of trials
await recorder.appendToJatos();

storeInStudySession()​

Stores session in JATOS study session data. Use this to share session data across components in a multi-component study.

storeInStudySession(): void
// in component 1
recorder.storeInStudySession();
jatos.startNextComponent();

loadFromStudySession()​

Loads previous session data from JATOS study session. Use this in multi-component studies to retrieve data from previous components.

loadFromStudySession(): SlopitSession | undefined
// in component 2
const previousSession = recorder.loadFromStudySession();
if (previousSession !== undefined) {
console.log(`Previous session had ${previousSession.trials.length} trials`);
}

exportSession()​

Exports the session data in SlopitSession format.

exportSession(): SlopitSession

getResultKey()​

Gets the result key used for JATOS data storage.

getResultKey(): string

Other Methods​

Inherited from BaseRecorder:

  • startTrial(config) - Start a new trial (use beginTrial for convenience)
  • endTrial(response?, platformData?) - End current trial (use finishTrial for convenience)
  • attachCapture(element, startTime?) - Attach capture to an element
  • detachCapture() - Detach capture without ending trial
  • getCurrentTrial() - Get current trial state
  • getTrials() - Get all completed trials
  • isTrialInProgress() - Check if trial is active
  • getSessionId() - Get session identifier
  • reset() - Reset for a new session

Complete JATOS Example​

Single Component Study​

<!DOCTYPE html>
<html>
<head>
<script src="jatos.js"></script>
<script type="module">
import { SlopitRecorder } from "@slopit/adapter-jatos";

let recorder = null;

jatos.onLoad(async function() {
// initialize recorder (auto-detects participant and study IDs)
recorder = new SlopitRecorder();

// create trial UI
document.body.innerHTML = `
<h2>Writing Task</h2>
<p>Please describe your ideal vacation destination.</p>
<textarea id="response" rows="6" style="width: 100%;"></textarea>
<button id="submit">Submit</button>
`;

const textarea = document.getElementById("response");
const submitBtn = document.getElementById("submit");

// start capture
recorder.beginTrial("writing_1", textarea, {
trialType: "free-response",
stimulus: {
type: "text",
content: "Describe your ideal vacation destination.",
},
});

// handle submit
submitBtn.addEventListener("click", async function() {
// end trial
recorder.finishTrial(textarea.value);

// submit to JATOS
await recorder.submitToJatos();

// show thank you message
document.body.innerHTML = `
<h2>Thank You</h2>
<p>Your response has been recorded.</p>
`;

// end study after a delay
setTimeout(function() {
jatos.endStudy();
}, 2000);
});
});
</script>
</head>
<body>
<p>Loading...</p>
</body>
</html>

Multi-Component Study​

Component 1: Instructions and First Task

import { SlopitRecorder } from "@slopit/adapter-jatos";

jatos.onLoad(function() {
const recorder = new SlopitRecorder({
metadata: { component: 1 },
});

// ... run first task ...

recorder.beginTrial("task1_trial1", textarea);
// ... participant completes trial ...
recorder.finishTrial(response);

// store session for next component
recorder.storeInStudySession();

// move to next component
jatos.startNextComponent();
});

Component 2: Second Task

import { SlopitRecorder } from "@slopit/adapter-jatos";

jatos.onLoad(async function() {
const recorder = new SlopitRecorder({
metadata: { component: 2 },
});

// load data from previous component
const previousSession = recorder.loadFromStudySession();
console.log(`Previous component completed ${previousSession?.trials.length} trials`);

// ... run second task ...

recorder.beginTrial("task2_trial1", textarea);
// ... participant completes trial ...
recorder.finishTrial(response);

// submit all data
await recorder.submitToJatos();

jatos.endStudy();
});

Long Experiment with Incremental Saves​

import { SlopitRecorder } from "@slopit/adapter-jatos";

jatos.onLoad(async function() {
const recorder = new SlopitRecorder();

const blocks = [
{ trials: ["t1", "t2", "t3"] },
{ trials: ["t4", "t5", "t6"] },
{ trials: ["t7", "t8", "t9"] },
];

for (const block of blocks) {
for (const trialId of block.trials) {
// run trial
await runTrial(recorder, trialId);
}

// save after each block
await recorder.appendToJatos();
console.log(`Block saved`);
}

// final submission
await recorder.submitToJatos();
jatos.endStudy();
});

async function runTrial(recorder, trialId) {
return new Promise((resolve) => {
const textarea = document.getElementById("response");
recorder.beginTrial(trialId, textarea);

document.getElementById("submit").onclick = function() {
recorder.finishTrial(textarea.value);
resolve();
};
});
}

Types​

JatosRecorderConfig​

Configuration for the JATOS recorder.

interface JatosRecorderConfig extends RecorderConfig {
resultKey?: string; // default: "slopit"
}

JatosModule​

Interface for the global JATOS module (available as window.jatos).

interface JatosModule {
onLoad(callback: () => void): void;
submitResultData(data: unknown): Promise<void>;
appendResultData(data: string): Promise<void>;
endStudy(successful?: boolean, message?: string): void;
startNextComponent(message?: string): void;
studySessionData: Record<string, unknown>;
componentResultData: unknown;
studyResultData: unknown;
workerId: string;
studyId: string;
batchId: string;
componentId: string;
}

Platform Notes​

  • JATOS does not expose version information client-side; platform version is reported as "unknown"
  • The jatos global object must be available (load jatos.js first)
  • submitToJatos() replaces existing result data; use appendToJatos() for incremental saves
  • Study session data persists across components in multi-component studies
  • Use resultKey to customize the key under which slopit data is stored

Data Access in JATOS​

After running your study:

  1. Go to your study in the JATOS GUI
  2. Click Results
  3. Export results as JSON or CSV
  4. The slopit session will be under the configured resultKey (default: "slopit")
import json
import pandas as pd

# for JSON export
with open("results.json") as f:
results = json.load(f)

for result in results:
data = json.loads(result["data"])
slopit_session = data.get("slopit")
if slopit_session:
print(f"Session {slopit_session['sessionId']}")
for trial in slopit_session["trials"]:
print(f" Trial {trial['trialId']}: {len(trial['behavioral']['keystrokes'])} keystrokes")