jpPsych ver 8へのp5.jsの対応バージョン
#tips #jsPsych #p5js
前回の記事(jpPsychとp5js(ver7)汎用バージョン)から、新たにjsPsych ver8.x.xが登場しており、いくつか大きな仕様変更がされていました。その辺はここを見てください。特にTimelineの変更が影響して、jsPsych.timelineVariable()ではうまく変数を呼び出せなくて苦労してたところに、Boss (@kohske) がjsPsych ver8.xとp5.jsを組み合わせて使えるプラグインの修正バージョンを書いてくれたのでここにメモとして残します。ちなこれ書いているのはパスダックのよしむらです。
今回使ったバージョン
※p5についてはver2.xでも動きますが、sketch内での記法も合わせて変更しないといけないので,ここはver1.x
でやります。
jsPsych ver 8.2.2
p5.js ver 1.11.11
前のv7の時と同じで、p5.jsの描画関数を呼び出して画面に刺激として表示したりできるプラグインです。v8では、timelineとプラグイン間のtrialデータの扱いに関する部分が変更されているようで(詳しくは公式)、その辺に対応した変更をしてます。
code:jspsych-p5.js
var jsPsychP5 = (function (jspsych) {
"use strict";
let jsPsychP5_p5;
const info = {
name: "p5",
version: "0.1",
parameters: {
trial_duration: {
type: jspsych.ParameterType.INT,
default: null,
},
sketch: {
type: jspsych.ParameterType.FUNCTION,
default: undefined,
},
end_trial: {
type: jspsych.ParameterType.FUNCTION,
default: null,
},
},
data: {
}
};
/**
* ** p5 **
*
* plugin for use p5.js
*
* @author @kohske
*/
class jsPsychP5Plugin {
constructor(jsPsych) {
this.jsPsych = jsPsych;
}
trial(display_element, trial) {
let end_trial = function (data) {
jsPsych.pluginAPI.clearAllTimeouts();
display_element.innerHTML = "";
jsPsychP5_p5.remove();
jsPsych.finishTrial(data);
};
trial.end_trial = end_trial;
if (trial.trial_duration !== null) {
jsPsych.pluginAPI.setTimeout(function () {
end_trial();
}, trial.trial_duration);
}
let s = trial.sketch;
jsPsychP5_p5 = new p5(s, display_element);
jsPsychP5_p5.trial = trial;
}
}
jsPsychP5Plugin.info = info;
return jsPsychP5Plugin;
})(jsPsychModule);
使用例
v7の記事の時とほぼ同じもので、2種類の調整法課題を3試行ずつ実施するものです。主な変更点はtrialデータを投げる時の書き方の部分くらいです。
フォルダは以下のようになる想定です。
code: ファイル構成
experiment
┣ demo01.html
┣ demo01.js
┗ jspsych-pligin
┗ jspsych-p5.js
htmlファイルです。demo01.html`とします
code: demo01.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My experiment</title>
<script src="https://unpkg.com/jspsych@8.2.2"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.11/lib/p5.min.js"></script>
<script src="jspsych-plugin/jspsych-p5.js"></script>
<link href="https://unpkg.com/jspsych@8.2.2/css/jspsych.css" rel="stylesheet" type="text/css" />
<script src="demo01.js"></script>
</head>
<body></body>
</html>
Javascriptのファイルです。demo01.jsとします。
code: demo01.js
const jsPsych = initJsPsych({
on_finish: function() {
jsPsych.data.displayData();
}
});
// スケッチ関数。中身はp5.jsのエディタの中身でOK。
// p5.jsのオブジェクトにはprefixでp.を付ける必要があります。
// 1個目
let s_luminance = function (p) {
// これはおまじないとして入れておく。
let trial = jsPsych.getCurrentTrial();
// タイムラインのデータを拾う(jsPsychのサイトを参照)。
// ここでは背景色を変える例。
// ver8では、新たにjsPsych.evaluateTimelineVariable()で呼び出す必要がある。
let bg = jsPsych.evaluateTimelineVariable("bg", true);
let button, slider;
p.setup = function () {
p.createCanvas(400, 400);
p.createP("背景と同じ色にしてクリック");
// スライダーを生成
slider = p.createSlider(0, 255, 100);
slider.style('width', '80px');
// ボタンを生成
button = p.createButton('決定');
button.mousePressed(
function(){
// 新たなプラグインでは保存するデータは以下のような連想配列で格納してend_trialに渡す必要がある。
let data = {
response: slider.value(),
stimulus: bg
};
// アクションでトライアルを終わらせる場合は、こうする。
p.trial.end_trial(data);
}
);
};
p.draw = function () {
p.background(bg)
p.noStroke();
p.rectMode(p.CENTER);
p.fill(255, 0, 0);
p.rect(200, 200, 70);
p.fill(slider.value());
p.rect(200, 200, 50);
};
}; // end sketch
// p5のluminanceトライアルを定義
let p5_trial_luminance = {
type: jsPsychP5,
// trial_duration: 1000, // 時間で終わらせるなら、時間指定。
sketch: s_luminance, // このトライアル用のスケッチ(関数)を指定
data: {
response: null,
}
};
// 2個目
let s_angle = function (p) {
// これはおまじないとして入れておく。
let trial = jsPsych.getCurrentTrial();
// タイムラインのデータを拾う(使い方はver8のjsPsychのサイトを参照)。
let angle = jsPsych.evaluateTimelineVariable("angle", true);
let x;
let button, slider;
p.setup = function () {
p.createCanvas(400, 400);
p.createP("同じ傾きにしてクリック");
button = p.createButton('決定');
button.mousePressed(
function(){
// データの保存
let data = {
response: slider.value(),
stimulus: angle
};
// アクションでトライアルを終わらせる場合は、こうする。
p.trial.end_trial(data);
}
);
slider = p.createSlider(-45, 45, 0);
slider.style('width', '80px');
};
p.draw = function () {
p.background(255)
p.stroke(0);
p.strokeWeight(5);
p.push();
p.translate(300, 200);
p.rotate(p.PI*slider.value()/180);
p.line(0, -50, 0, 50);
p.pop();
p.push();
p.translate(100, 200);
p.rotate(p.PI*angle/180);
p.line(0, -50, 0, 50);
p.pop();
};
}; // end sketch
// p5のangleトライアルを定義
let p5_trial_angle = {
type: jsPsychP5,
sketch: s_angle, // このトライアル用のスケッチ(関数)を指定
data: {
response: null,
}
};
// タイムラインを定義
let luminance_block = {
timeline: p5_trial_luminance,
// 0~2 の配列をシャッフル、ループに合わせて type に配列を順番に代入
// ようするに{bg: 0}, {bg: 128}, {bg: 255}
timeline_variables: [...64, 128, 192].map((d) => {
return { bg: d };
}),
repetitions: 1,
randomize_order: true,
};
// タイムライン
let angle_block = {
timeline: p5_trial_angle,
timeline_variables: [...-5, 0, 5].map((d) => {
return { angle: d };
}),
repetitions: 1,
randomize_order: true,
};
jsPsych.run(luminance_block, angle_block);
よろしくメカドック