上一篇教程中,我们做了一个简单的实验程序 demo,演示了一下 jsPsych 编写实验程序的基本逻辑。这篇教程,我们会在上一篇教程的基础上,进行扩展和重构,把它做成一个完整的、像模像样的实验程序。
这篇教程的主要目的是认识 timeline 这一重要的组件,并学会用它简化实验程序的设计,或实现额外的功能。
0. 提前规划——我们要做什么?
我们再看看上一篇教程中我们完成的实验程序代码:
let jsPsych = initJsPsych();
let instruction = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `
<p>在实验中,屏幕中央会呈现一个圆形</p>
<p>如果呈现的是蓝色圆形,请尽快按 F 键</p>
<p>如果呈现的是橙色圆形,请尽快按 J 键</p>
<p>按任意键开始实验</p>
`,
post_trial_gap: 500
}
let blue_trial = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `<img src="./images/blue.png">`,
choices: ['f','j'],
post_trial_gap: 500
}
let orange_trial = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `<img src="./images/orange.png">`,
choices: ['f','j'],
post_trial_gap: 500
}
jsPsych.run([
instruction,
blue_trial, orange_trial, blue_trial, orange_trial, blue_trial, orange_trial
])
很显然,其中最大的问题有二:
- 试次顺序固定,没有实际意义,且在运行时过于繁琐;
- 在反应时实验中,通常需要使用“线索”(cue)来提示被试,马上会出现刺激,这个实验中没有做到。
当然,还有其他各种各样的小问题,不过在这篇文章中,我们主要集中在上面两个问题。
那么,我们先大概规划一下,改进后的实验应该做到什么程度呢?
- 有 60 个试次,橙蓝各 30 个,试次的呈现顺序随机;
- 在每个试次开始前,要有 500 ms 的提示点,500 ms 的空屏。
1. 从简单的开始——组织一个 timeline
首先我们来实现提示点和空屏的问题。还是拿 PsychoPy 举个例子:在 PsychoPy 的 trial 里,想要实现这个功能,大概率是这样的一个结构:
当然,我们已经知道了,jsPsych 中的 trial 比 PsychoPy 里的离散很多,是一个更小的功能单元。把两个 trial 连接起来,就需要使用 timeline 了。
Timeline(时间线)是一种可以用来组织各个 trial 的顺序,把多个 trial 合并到同一流程内的组件。
1.1 创建 timeline
先让我们把上面示例中的橙蓝刺激改成 timeline 看看。
let blue_timeline = {
timeline: [
{
type: jsPsychHtmlKeyboardResponse,
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
post_trial_gap: 500
},
{
type: jsPsychHtmlKeyboardResponse,
stimulus: `<img src="./images/blue.png">`,
choices: ['f','j'],
post_trial_gap: 500
}
]
}
let orange_timeline = {
timeline: [
{
type: jsPsychHtmlKeyboardResponse,
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
post_trial_gap: 500
},
{
type: jsPsychHtmlKeyboardResponse,
stimulus: `<img src="./images/orange.png">`,
choices: ['f','j'],
post_trial_gap: 500
}
]
}
基于这个示例,一个 timeline 的基本结构就成型了:它把两个单独的 trial 连接了起来。我们给前一个注视点的刺激规定了试次的时间长度(500 ms)和它后续的 gap 长度(500 ms)。连接试次是 timeline 的基本功能。现在,我们替换一下运行试次的代码:
jsPsych.run([
instruction,
blue_timeline, orange_timeline, blue_timeline, orange_timeline, blue_timeline, orange_timeline
])
注视点和空屏的功能就完成了!
1.2 利用 timeline 简化实验代码
Timeline 还可以起到简化实验代码的作用——你可以把重复的部分抽离出来,放到 timeline 里面,timeline 会自动把它们填入试次里。比如说,我们可以这样:
let blue_timeline = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500
},
{
stimulus: `<img src="./images/blue.png">`,
choices: ['f','j']
}
]
}
let orange_timeline = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500
},
{
stimulus: `<img src="./images/orange.png">`,
choices: ['f','j']
}
]
}
我们把定义试次类型(type
)和规定试次后空屏(post_trial_gap
)的部分抽离了出来,放在了和 timeline 同级的地方。这样做之后,代码仍然可以正常执行,和修改前的效果是一致的。这意味着,我们可以把繁复的代码中的共同部分剥离出来,减少我们编辑每一个 trial 的工作量。
当然,到这里你肯定会有一个问题:如果同一个参数,我在 timeline 中定义了一次,又在单独的 trial 里定义了一次,会发生什么呢?
为什么不自己试试呢?(笑)
let blue_timeline = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
post_trial_gap: 2000
},
{
stimulus: `<img src="./images/blue.png">`,
choices: ['f','j']
}
]
}
let orange_timeline = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
post_trial_gap: 2000
},
{
stimulus: `<img src="./images/orange.png">`,
choices: ['f','j']
}
]
}
在以上的代码中,我们在 timeline 中指定了 post_trial_gap
这一参数为 500,又在线索提示点的 trial 里指定了 2000。把这段代码运行一下,它最后的结果是 500 ms,还是 2000 ms 呢?
答案是 2000 ms。在 trial 中定义的参数具有更高的优先级,会覆盖掉 timeline 的默认参数。
2 深入研究——认识时间线变量
我们解决了如何组合一个完整的刺激流程的问题,接下来,让我们开始接触 timeline 的更多功能,实现我们的第一个目标:重复并随机试次。
到这里,就要介绍时间线变量(timeline variables)了。先上示例代码:
// Timeline Variables Demo
let timeline_demo = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
},
{
stimulus: jsPsych.timelineVariable('picture'),
choices: ['f','j']
}
],
timeline_variables: [
{picture: '<img src="./images/blue.png">'},
{picture: '<img src="./images/orange.png">'},
]
}
这段代码在我们写的两个 timeline 基础上做了更进一步的抽象化,把两个 timeline 统一成了一个。
在 trial 中,我们将刺激指定为一个变量 picture
,随后在参数中将 picture
的内容定义好——在这里是两张图片。jsPsych 会自动按顺序执行完所有的变量,再结束实验。所以如果运行这个实验,你会分别接受一次蓝色圆刺激,一次橙色圆刺激。
使用时间线变量,可以方便地定义试次中的各种内容,进一步简化我们的代码复杂程度。
3. 循环和随机化——时间线的采样方法
再下一步,那自然是试次的重复和随机化了。Timeline 中有一个独特的参数,用来控制时间线的循环和随机化:
// Timeline Variables Demo
let timeline_demo = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
},
{
stimulus: jsPsych.timelineVariable('picture'),
choices: ['f','j']
}
],
timeline_variables: [
{picture: '<img src="./images/blue.png">'},
{picture: '<img src="./images/orange.png">'},
],
sample: {
type: 'fixed-repetitions',
size: 30
}
}
注意我们新增的 sample
参数,这规定了试次的“采样方法”——如何选取试次,试次用什么方式运行。在这里,我们选择了 fixed-repetitions
,要求试次以随机的顺序总共重复 30 次。
再保存一下,运行试试?
4. 总结
本篇教程结束后,完整的实验程序代码如下:
let jsPsych = initJsPsych();
let instruction = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `
<p>在实验中,屏幕中央会呈现一个圆形</p>
<p>如果呈现的是蓝色圆形,请尽快按 F 键</p>
<p>如果呈现的是橙色圆形,请尽快按 J 键</p>
<p>按任意键开始实验</p>
`,
post_trial_gap: 500
}
// Timeline Variables Demo
let timeline_demo = {
type: jsPsychHtmlKeyboardResponse,
post_trial_gap: 500,
timeline: [
{
stimulus: `+`,
choices: ["NO_KEYS"],
trial_duration: 500,
},
{
stimulus: jsPsych.timelineVariable('picture'),
choices: ['f','j']
}
],
timeline_variables: [
{picture: '<img src="./images/blue.png">'},
{picture: '<img src="./images/orange.png">'},
],
sample: {
type: 'fixed-repetitions',
size: 30
}
}
jsPsych.run([
instruction,
timeline_demo
])
到这里,你已经知道了 jsPsych 程序的基本编写原理——当然,这篇教程太浅了(毕竟是快速上手,以入门了解基本方法为主)。你需要补充阅读原始的教程来了解时间线的控制方法究竟有多少种,有哪些可以使用的东西。
jsPsych 的 Timeline 官方教程:https://www.jspsych.org/7.3/overview/timeline/
下一篇教程将从实战出发,从头开始设计一个实验,主要讲解设计 jsPsych 实验的思路——应该从什么角度开始思考?选择哪些东西?如何组合它们?等等等等。
此外,你也可以阅读这个实验的原始教程:https://www.jspsych.org/7.3/tutorials/rt-task/,看看官方的实现方法和我们教程中的做法有哪些不同,这些不同有哪些影响。
照例,在本文末尾留几个习题,作为进一步的思考。
习题 1
为了避免被试的习惯化,通常我们要求线索之后的空屏有一定的随机量(举例而言,400 - 600 ms)。如何实现这一点呢?
- 你需要学习的:jsPsych 的动态参数
习题 2
如果你完成了上一篇教程中的习题 2(修改元素的风格格式),那么请完成:增大指导语的字体大小到 200%,增大注视点的刺激大小到 400%。
- 你需要学习的:CSS 的编辑