Python SDK (psycloudpy)
psycloudpy lets you author a complete study in Python with a fluent, chainable builder. It
compiles to the same bundle format Studio produces, so a code-authored study runs on the same
runtime and can be opened in Studio for further editing.
The SDKs ship inside the PsyCloud monorepo and are under active development (psycloudpy is
0.0.1, not yet on PyPI). Clone the repo and install in editable mode:
pip install -e packages/psycloudpyA complete example
This builds a minimal Stroop study end-to-end — factors, a screen, a keypress response — and writes a runnable bundle to disk:
from psycloudpy import experiment, text, keypress
from psycloudpy.expr import col
bundle = (
experiment(id="hello.stroop", name="Hello Stroop")
.phase("main", label="Trials")
.factors(word=["red", "blue"], ink=["red", "blue"])
.cross()
.shuffle()
.screen("trial")
.present(text("+"), 250)
.ask(
text(col.word, fill=col.ink, font_size=72),
keypress(keys=["r", "b"]),
)
.bind_self("word", "ink")
.done()
.bundle(with_auto_ids=True)
)
from psycloudpy import pc_bundle_write
pc_bundle_write(bundle, "stroop_bundle", overwrite=True)That stroop_bundle/ directory can be served, compiled, or imported into Studio — see
the CLI reference and Interoperability.
The fluent chain
Authoring flows through three chained builders. Each .method() returns the next builder, so a
study reads top to bottom:
- experiment(…) → project
Start a study.
experiment(id, name)returns the project builder. Project-level helpers include.hypothesis(),.within_subjects(),.between_subjects(),.measures(),.instructions_page(), and.end_page(), plus flow control (.run(),.repeat_block(),.cycle_block(),.branch_block(),.loop_until()). - .phase(…) → trial generation
.phase(id, label)opens a phase. Build its trial table with.factors(),.cross(),.shuffle(),.repeat(),.sample(),.per_block(),.derive(),.filter(),.constrain(),.assign_between(), or load rows directly with.trials(data). - .screen(…) → what participants see
.screen(id)opens the trial template. Sequence it with.present(stimulus, duration_ms),.ask(stimulus, response),.feedback(...), and.pause(...). Choose which columns to record with.bind_self(...)/.record(...). - .done().bundle(…) → compile
.done()closes the screen back to the project;.bundle(with_auto_ids=True)validates and produces the bundle. (.bundle()on the screen is shorthand for.done().bundle().)
Stimulus builders include text, image, fixation_cross, blank, markdown, group, and
slider; response builders include keypress, slider, button_response, prompt,
likert_scale, multichoice, image_choice, and anykey.
Dynamic values: the expression DSL
Static values are fine for fixed stimuli, but most studies need values that depend on the trial,
the response, or running state. Import these from psycloudpy.expr:
from psycloudpy.expr import col, row, state, outcome, const
from psycloudpy.expr import expr_if, case_when, uppercase| Reference | Means |
|---|---|
col.word | the current trial's word value at run time |
row.word | the design-time row value (resolved before run time) |
outcome.correct, outcome.rt, outcome.timedOut | response results (after a response) |
state.streak | a phase-scoped counter you maintain |
const.max_rt | a study-level constant set at participant entry |
Operators work naturally (==, <, +, &, |, ~), and helpers like expr_if, case_when,
uppercase, lowercase, coalesce, and one_of cover common logic. For example, conditional
feedback:
.feedback(
text(case_when(
(outcome.correct, "Correct!"),
(outcome.timedOut, "Too slow"),
default="Incorrect",
)),
timeout(1000),
)Use col.x for values resolved at run time (the participant's actual trial) and row.x for
values computed at design time while the trial table is being built. They look similar but
resolve at different moments.
From bundle to running study
from psycloudpy import write_bundle, check, simulate_tables
# Write the bundle directory (validates as it writes)
write_bundle(bundle, dir="./stroop_bundle", overwrite=True)
# Compile + lint without leaving Python
report = check("./stroop_bundle")
# Dry-run participant sessions to sanity-check the design
tables = simulate_tables("./stroop_bundle", n_participants=10)A bundle directory contains bundle.json, designProgram.json, screenProgram.json,
bindings.json, and (when used) counterbalancePolicy.json. From there you can serve or compile
it with the CLI, or import it into Studio.
write_bundle, check, and simulate_tables run entirely offline. Pushing a study to a hosted
server (pc_push / pc_push_from_path) requires server credentials
(PSYCLOUD_SERVER_URL, PSYCLOUD_API_KEY).
Worked examples
The package ships runnable example studies under packages/psycloudpy/src/psycloudpy/examples/high_level/ —
including color_stroop, simple_rt, recognition_memory, continuous_recognition,
panas_survey, matrix_reasoning, adaptive_practice, luck_vogel_1997, and a design-first
memory study. The docs/tutorials/ folder maps nine jsPsych paradigms to psycloudpy.