Skip to content

Save universal parameters listed in save_trial_parameters - #3722

Open
rmz-oz wants to merge 2 commits into
jspsych:mainfrom
rmz-oz:fix-save-universal-params
Open

rmz-oz wants to merge 2 commits into
jspsych:mainfrom
rmz-oz:fix-save-universal-params

Conversation

@rmz-oz

@rmz-oz rmz-oz commented Sep 26, 2026 •

Copy link
Copy Markdown

save_trial_parameters only looked at the plugin's own info.parameters, so universal parameters (css_classes, post_trial_gap, on_start, ...) were rejected with Non-existent parameter "post_trial_gap" specified in save_trial_parameters. and left out of the data, although the example in docs/overview/plugins.md saves post_trial_gap this way.

Following @bjoluc's review, universal parameters are now evaluated in processParameters and added to trialObject:

  • Each universal parameter that the plugin doesn't define itself is evaluated like a plugin parameter. Timeline variables and inherited timeline values are resolved, functions are called unless the parameter type is FUNCTION, and missing values get their default. An unset post_trial_gap gets default_iti, since that is the gap that is actually used.
  • Plugins now receive these values in the trial object too.
  • save_trial_parameters reads them from trialObject like plugin parameters. The warning is still shown for names that are neither plugin nor universal parameters.
  • data is left out on purpose. Its properties are still evaluated when the result is created.
  • Parameter lookups are cached, so the trial uses the same values that end up in the data.

One behavior change: a function passed to post_trial_gap used to be called after the trial finished. Now it is called before the trial starts, together with the other dynamic parameters. This matches docs/overview/dynamic-parameters.md, but I can keep the old timing if you prefer.

Tests:

  • New unit tests in Trial.spec.ts: plugins receive the evaluated universal parameters, they can be saved and removed with save_trial_parameters without a warning, and the default_iti fallback works. Four existing tests that compare the whole trial object now include the default universal values.
  • Integration test in tests/data/trialparameters.test.ts with html-keyboard-response, based on the case from the issue.
  • With these tests on the old Trial.ts, 8 fail. With the change, npx jest --ci gives 739 passed, 6 skipped, 80 suites. npm run tsc and npm run build pass (59/59 tasks each).

AI use: I used Claude Code to write the code and the tests. I picked the issue, reproduced it, and reviewed and ran the changes myself.

Includes a patch changeset for jspsych and adds me to contributors.md.

Fixes #3501

🤖 Generated with Claude Code

save_trial_parameters only accepted names found in the plugin's info
object. Parameters that every plugin accepts, like css_classes and
post_trial_gap, were rejected with a "Non-existent parameter" warning
and never ended up in the data, even though the docs example saves
post_trial_gap this way.

Universal parameters are now resolved the same way the trial uses
them: timeline variables and functions are evaluated, callbacks are
saved as strings, an unset post_trial_gap falls back to default_iti,
and data is saved with its properties evaluated. Setting one of them
to false removes it from the data.

Fixes jspsych#3501
@changeset-bot

changeset-bot Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 174e146

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
jspsych Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📦 Preview build ready

Built from PR head f764a2b and published at 8b5c740 on branch preview/pr-3722.
URLs below are pinned to an immutable commit SHA, so they are safe to share and are cached permanently by jsDelivr.

Changed packages: jspsych

Quick-start HTML:

<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/jspsych/dist/index.browser.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/jspsych/css/jspsych.css">
<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-keyboard-response/dist/index.browser.min.js"></script>
All package URLs
  • @jspsych/extension-mouse-tracking → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/extension-mouse-tracking/dist/index.browser.min.js
  • @jspsych/extension-pipe → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/extension-pipe/dist/index.browser.min.js
  • @jspsych/extension-record-video → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/extension-record-video/dist/index.browser.min.js
  • @jspsych/extension-webgazer → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/extension-webgazer/dist/index.browser.min.js
  • jspsych → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/jspsych/dist/index.browser.min.js
  • @jspsych/plugin-animation → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-animation/dist/index.browser.min.js
  • @jspsych/plugin-audio-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-audio-button-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-audio-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-audio-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-browser-check → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-browser-check/dist/index.browser.min.js
  • @jspsych/plugin-call-function → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-call-function/dist/index.browser.min.js
  • @jspsych/plugin-canvas-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-canvas-button-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-canvas-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-canvas-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-categorize-animation → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-categorize-animation/dist/index.browser.min.js
  • @jspsych/plugin-categorize-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-categorize-html/dist/index.browser.min.js
  • @jspsych/plugin-categorize-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-categorize-image/dist/index.browser.min.js
  • @jspsych/plugin-cloze → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-cloze/dist/index.browser.min.js
  • @jspsych/plugin-external-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-external-html/dist/index.browser.min.js
  • @jspsych/plugin-free-sort → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-free-sort/dist/index.browser.min.js
  • @jspsych/plugin-fullscreen → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-fullscreen/dist/index.browser.min.js
  • @jspsych/plugin-html-audio-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-audio-response/dist/index.browser.min.js
  • @jspsych/plugin-html-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-button-response/dist/index.browser.min.js
  • @jspsych/plugin-html-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-html-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-html-video-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-html-video-response/dist/index.browser.min.js
  • @jspsych/plugin-iat-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-iat-html/dist/index.browser.min.js
  • @jspsych/plugin-iat-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-iat-image/dist/index.browser.min.js
  • @jspsych/plugin-image-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-image-button-response/dist/index.browser.min.js
  • @jspsych/plugin-image-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-image-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-image-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-image-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-initialize-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-initialize-camera/dist/index.browser.min.js
  • @jspsych/plugin-initialize-microphone → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-initialize-microphone/dist/index.browser.min.js
  • @jspsych/plugin-instructions → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-instructions/dist/index.browser.min.js
  • @jspsych/plugin-maxdiff → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-maxdiff/dist/index.browser.min.js
  • @jspsych/plugin-mirror-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-mirror-camera/dist/index.browser.min.js
  • @jspsych/plugin-preload → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-preload/dist/index.browser.min.js
  • @jspsych/plugin-reconstruction → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-reconstruction/dist/index.browser.min.js
  • @jspsych/plugin-resize → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-resize/dist/index.browser.min.js
  • @jspsych/plugin-same-different-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-same-different-html/dist/index.browser.min.js
  • @jspsych/plugin-same-different-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-same-different-image/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-time-mouse → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-serial-reaction-time-mouse/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-time → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-serial-reaction-time/dist/index.browser.min.js
  • @jspsych/plugin-sketchpad → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-sketchpad/dist/index.browser.min.js
  • @jspsych/plugin-survey-html-form → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey-html-form/dist/index.browser.min.js
  • @jspsych/plugin-survey-likert → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey-likert/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-choice → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey-multi-choice/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-select → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey-multi-select/dist/index.browser.min.js
  • @jspsych/plugin-survey-text → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey-text/dist/index.browser.min.js
  • @jspsych/plugin-survey → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-survey/dist/index.browser.min.js
  • @jspsych/plugin-video-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-video-button-response/dist/index.browser.min.js
  • @jspsych/plugin-video-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-video-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-video-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-video-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-virtual-chinrest → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-virtual-chinrest/dist/index.browser.min.js
  • @jspsych/plugin-visual-search-circle → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-visual-search-circle/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-calibrate → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-webgazer-calibrate/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-init-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-webgazer-init-camera/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-validate → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@8b5c740ba7e6a6fbb55ac717a9579b2bfbe7c3ba/packages/plugin-webgazer-validate/dist/index.browser.min.js

Last updated 2026-09-26 21:02 UTC for PR head f764a2b.

@bjoluc

bjoluc commented Sep 26, 2026

Copy link
Copy Markdown
Member

Thanks for flagging this @rmz-oz and for proposing a fix. I think I've missed this while rebuilding the core library a couple of years ago.
Re your solution: In my opinion, we shouldn't remove the error message in general: It's useful for parameters that are really not specified anywhere. Universal plugin parameters are well-defined, so instead of adding a second lookup level (getUniversalParameterValue) here, I'd prefer to properly evaluate them and add them to the trialObject in `processParameters, s.t. the plugin has access to them as well. That might require some more effort, so let us know if you'd like to work on it or if someone else should take the wheel from here. Cheers!
PS: For transparency, it would be great if you could mention the use of Claude in PR descriptions and which part of the work is yours vs Claude's.

Instead of looking up universal parameters separately when saving the
data, processParameters now evaluates them and adds them to
trialObject, so plugins can access them too. save_trial_parameters
reads them from trialObject like any other parameter, and the
"Non-existent parameter" warning stays for names that are neither
plugin nor universal parameters.

An unset post_trial_gap gets the default_iti value. The data parameter
is left as it was, since its properties are evaluated when the result
is created.
@rmz-oz

rmz-oz commented Sep 27, 2026

Copy link
Copy Markdown
Author

Thanks for the review! I've updated the PR as you suggested. Universal parameters are now evaluated in processParameters and added to trialObject, and getUniversalParameterValue is gone. The warning stays for parameters that aren't defined anywhere.

One thing I'd like your opinion on: a function passed to post_trial_gap is now called before the trial starts instead of after it ends. That matches the dynamic parameters docs, but it is still a change, so I can keep the old timing if you prefer. I also left data out, so its properties are still evaluated when the result is created.

I've also added a note about using Claude Code to the description. Thanks for pointing that out.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

default parameters aren't saved in save_trial_parameters

2 participants