Requesting feedback on a relevant jsPsych library #732
Replies: 2 comments 2 replies
-
Hi @vijaymarupudi ! Sorry it has taken me a couple of days to respond to this. I needed to make sure I had enough time to think about it and write a reply. Here are my reactions, in no particular order:
|
Beta Was this translation helpful? Give feedback.
-
One of my goals for a future version (probably v8.0) is to rewrite the backend of timeline management so that inserting trials dynamically is possible. This would be a moment to create a system that is more compatible with generators or async functions.
Certainly there are going to be designs that are easier to express in one version or the other. That's a great reason to have an alternative mode of constructing timelines. It may also depend on how the experimenter prefers to think about the experiment. Some folks may like expressing things in a declarative mode and others may like the generative mode. My hope is that either mode would allow writing any experiment.
Got it, that makes sense. This would be a design philosophy change, but an interesting one to consider. It may be that moving some of the generic displays (image, audio, video, html) and generic responses (key, button, slider, text input) to a utility package would enable a plugin that can compose these different ideas together through its parameters, e.g.: var trial = {
type: 'generic-plugin',
stimulus: jsPsych.stimulus.image({ ... }),
response: jsPsych.stimulus.button({ ... })
} Changing things so that more than one plugin can be active at a time is a more radical change because it affects timeline design, data collection, and everything that depends on these systems.
I'm still queasy about adding a dependency here. I think the open design flexibility of jsPsych's plugin system is a feature. Adding structure to this then requires people writing plugins to think about the code in a particular way. For folks who know React it would be great. For those who don't there's a whole new system to learn. That said, I'm in total agreement that my implementation of many plugins is based around a particular style that may be difficult to follow (or may be easy, depending on how one likes to think about it). A lack of enforced structure certainly makes it harder to follow what other people's code is doing. Some of the specifics that you mention, like |
Beta Was this translation helpful? Give feedback.
-
[Cross-posting on Github]
Hello people interested in jsPsych,
For the past month I've been helping out people with their jsPsych problems, and have noticed several patterns in the errors that happen. People sometimes want to do something complicated involving a dynamic number of trials. People want to evaluate certain parameters at the time of the experiment rather than before it begins.
I believe a lot of this stems from a lack of understanding of the asynchronous nature of Javascript and the complexity of lifecycle callbacks. When jsPsych was first made, back in 2012, I believe there was no other way to deal with these problems. However the Javascript language and ecosystem have evolved in that time, and I'd like to incorporate those ideas to make designing online experiments easier.
I decided to write a library to do so. I was heavily inspired by jsPsych's elegance, and will continue to draw on it for code and ideas. It is very much in progress, but is currently functional. My primary goal was to eliminate the common questions by designing a different API. This required a department from the declarative nature of jsPsych towards a more runtime specified design. The Javascript feature I have focused on using are generator functions. They allow executation of code to be paused and played at specific points of the function.
Generator functions allow for a more intuitive flow of code that I think is easier for a beginner, a common user of jsPsych, to understand. Here is an example of a simple experiment that requires participants to click a button within 500ms of it being displayed. If they do not succeed, they would have to try again. In jsPsych, this would require a loop_function and an on_finish callback, however here it is in the library I have made.
Make sure you have jsPsych's js, css, and the appropriate plugin js loaded.
I have attached a fully working sample with the library code in this zip file. Click
index.html
to enjoy! If you'd like to explore the library code, check out thesrc/ReactionTime.ts
,src/reaction-time.ts
, and thesrc/jspsych-plugin.ts
files.Source code is also available at https://github.com/vijaymarupudi/reaction-time
I have named the library
ReactionTime
for now, hopefully that is not too confusing.reactionTime
is an interface to the library. I'd like opinions on what people think of this API. Also you may have noticed that I was using a jsPsych plugin! While the plugin system is a little different for my library, they can are compatible with each other.The code for this API is surprisingly simple and short. I have wrapper almost all jsPsych plugins (and most of jsPsych) within 100 lines of code. The library is written in Typescript for easier long term maintenance and useful editor hints in editors like Atom, VSCode, and Vim. I make liberal use of modern Javascript features (Promises & Async functions) knowing that I can transpile them to work in old browsers. Features like
Proxy
that aren't possible in older browsers have been avoided.A few notes on design philosophy. I have a design goal to keep this library simple and easy to maintain. I personally think that tasks that can be trivially performed in a loop should be done using loops. That would mean no timeline variables and no looping or conditional functions. I do realize that the declarative API that jsPsych provides makes it very easy to design simple experiments, I think the lack of an "escape hatch" makes it difficult to use for complicated ones that I have seen in the wild.
I think this api would make the follow features mentioned in the documentation redundant because generator functions make asynchronous code appear synchronous:
One con of this approach is that autopreloading is impossible. I think that's an acceptable tradeoff and that is it okay to ask the user to call the preload function with their list of media.
Aesthetically, I have completely avoided globals (such as depending on a single display_element being defined for the whole page) for the plugin API. I also plan on defering a lot of the work that's in
jspsych.js
to the plugins (specifically multimedia associated code). Given the modular design of the code, this would make it easier to maintain different plugins of the same media type.Wrapping jsPsych was an important goal for this library, as the work determining the nuances and quirks of browsers and multimedia playback is all stored in the code and I would not want to rewrite all of it. I hope for new plugins to be written using the
React
library with reusable hooks to maintain side-effects and state, but whether that will happen will depend on intersect.For future directions, I'd like to see if there's any interest at all. I plan on using this as a personal library to design some of the more complicated experiments, but if there's interest there are many options. One option would be to make this API the 'escape-hatch' in jsPsych ala
jsPsych.init_generator()
. Another not mutually exclusive option would be to publish it and maintain it alongside jsPsych.I think a lot of the value in jsPsych and probably the time spent on it was the documentation and tutorials. So if there's interest, I might spend some time on this API. Let me know your thoughts!
License: LGPL-3.0 or later
~ Vijay
Beta Was this translation helpful? Give feedback.
All reactions