Hi everyone,
Could be an online experiment based on the eeg_marker component with the Emotiv device?
JS version works without any error but the brainwaves don’t record on the Emotiv cloud.
Hi everyone,
Could be an online experiment based on the eeg_marker component with the Emotiv device?
JS version works without any error but the brainwaves don’t record on the Emotiv cloud.
Hi There,
I didn’t even know that you could run an emotiv study online in browser actually!! I don’t think the Emotiv components exist in PsychoJS, so I am not sure that would work.
Could you tell us more about what your current set up and code is?
Thanks,
Becca
Hi,
I used cortex_rec and eeg_marker in the Psychopy builder. I attached JS code that works in the web browser but the brainwaves don’t record on the Emotiv cloud. the interesting thing is that in the JS version I can see some codes about cortex_rec and event maker
/***********************
* Photo_Based_01 Test *
***********************/
import { core, data, sound, util, visual } from './lib/psychojs-2021.2.2.js';
const { PsychoJS } = core;
const { TrialHandler } = data;
const { Scheduler } = util;
//some handy aliases as in the psychopy scripts;
const { abs, sin, cos, PI: pi, sqrt } = Math;
const { round } = util;
// store info about the experiment session:
let expName = 'Photo_based_01'; // from the Builder filename that created this script
let expInfo = {'participant': '', 'session': '001'};
// Start code blocks for 'Before Experiment'
// init psychoJS:
const psychoJS = new PsychoJS({
debug: true
});
// open window:
psychoJS.openWindow({
fullscr: true,
color: new util.Color([0, 0, 0]),
units: 'height',
waitBlanking: true
});
// schedule the experiment:
psychoJS.schedule(psychoJS.gui.DlgFromDict({
dictionary: expInfo,
title: expName
}));
const flowScheduler = new Scheduler(psychoJS);
const dialogCancelScheduler = new Scheduler(psychoJS);
psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === 'OK'); }, flowScheduler, dialogCancelScheduler);
// flowScheduler gets run if the participants presses OK
flowScheduler.add(updateInfo); // add timeStamp
flowScheduler.add(experimentInit);
flowScheduler.add(trialRoutineBegin());
flowScheduler.add(trialRoutineEachFrame());
flowScheduler.add(trialRoutineEnd());
const trialsLoopScheduler = new Scheduler(psychoJS);
flowScheduler.add(trialsLoopBegin(trialsLoopScheduler));
flowScheduler.add(trialsLoopScheduler);
flowScheduler.add(trialsLoopEnd);
flowScheduler.add(quitPsychoJS, '', true);
// quit if user presses Cancel in dialog box:
dialogCancelScheduler.add(quitPsychoJS, '', false);
psychoJS.start({
expName: expName,
expInfo: expInfo,
resources: [
{'name': 'img/target.png', 'path': 'static/img/target.png'},
{'name': 'img/nontarget1.png', 'path': 'static/img/nontarget1.png'},
{'name': 'img/nontarget2.png', 'path': 'static/img/nontarget2.png'},
{'name': 'img/nontarget3.png', 'path': 'static/img/nontarget3.png'},
{'name': 'img/nontarget4.png', 'path': 'static/img/nontarget4.png'},
{'name': 'img/nontarget5.png', 'path': 'static/img/nontarget5.png'}
]
});
psychoJS.experimentLogger.setLevel(core.Logger.ServerLevel.EXP);
var frameDur;
async function updateInfo() {
expInfo['date'] = util.MonotonicClock.getDateStr(); // add a simple timestamp
expInfo['expName'] = expName;
expInfo['psychopyVersion'] = '2021.2.2';
expInfo['OS'] = window.navigator.platform;
// store frame rate of monitor if we can measure it successfully
expInfo['frameRate'] = psychoJS.window.getActualFrameRate();
if (typeof expInfo['frameRate'] !== 'undefined')
frameDur = 1.0 / Math.round(expInfo['frameRate']);
else
frameDur = 1.0 / 60.0; // couldn't get a reliable measure so guess
// add info from the URL:
util.addInfoFromUrl(expInfo);
return Scheduler.Event.NEXT;
}
var trialClock;
var cortex_rec;
var text;
var key_resp;
var StimuliClock;
var flage;
var image;
var eeg_marker;
var globalClock;
var routineTimer;
async function experimentInit() {
// Initialize components for Routine "trial"
trialClock = new util.Clock();
cortex_rec = {"status": "PsychoJS.Status.NOT_STARTED"};
text = new visual.TextStim({
win: psychoJS.window,
name: 'text',
text: 'Salam khosh amadi',
font: 'Open Sans',
units: undefined,
pos: [0, 0], height: 0.1, wrapWidth: undefined, ori: 0.0,
color: new util.Color('white'), opacity: undefined,
depth: -1.0
});
key_resp = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
// Initialize components for Routine "Stimuli"
StimuliClock = new util.Clock();
var flage,delay;
flage = 0;
image = new visual.ImageStim({
win : psychoJS.window,
name : 'image', units : undefined,
image : undefined, mask : undefined,
ori : 0.0, pos : [0, 0], size : [0.5, 0.5],
color : new util.Color([1, 1, 1]), opacity : undefined,
flipHoriz : false, flipVert : false,
texRes : 128.0, interpolate : true, depth : -1.0
});
eeg_marker = {"status": "PsychoJS.Status.NOT_STARTED"};
// Create some handy timers
globalClock = new util.Clock(); // to track the time since experiment started
routineTimer = new util.CountdownTimer(); // to track time remaining of each (non-slip) routine
return Scheduler.Event.NEXT;
}
var t;
var frameN;
var continueRoutine;
var _key_resp_allKeys;
var trialComponents;
function trialRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//------Prepare to start Routine 'trial'-------
t = 0;
trialClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
key_resp.keys = undefined;
key_resp.rt = undefined;
_key_resp_allKeys = [];
// keep track of which components have finished
trialComponents = [];
trialComponents.push(cortex_rec);
trialComponents.push(text);
trialComponents.push(key_resp);
for (const thisComponent of trialComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
var frameRemains;
function trialRoutineEachFrame() {
return async function () {
//------Loop for each frame of Routine 'trial'-------
// get current time
t = trialClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *text* updates
if (t >= 0.0 && text.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
text.tStart = t; // (not accounting for frame time here)
text.frameNStart = frameN; // exact frame index
text.setAutoDraw(true);
}
frameRemains = 0.0 + 5.0 - psychoJS.window.monitorFramePeriod * 0.75; // most of one frame period left
if (text.status === PsychoJS.Status.STARTED && t >= frameRemains) {
text.setAutoDraw(false);
}
// *key_resp* updates
if (t >= 0.0 && key_resp.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
key_resp.tStart = t; // (not accounting for frame time here)
key_resp.frameNStart = frameN; // exact frame index
// keyboard checking is just starting
psychoJS.window.callOnFlip(function() { key_resp.clock.reset(); }); // t=0 on next screen flip
psychoJS.window.callOnFlip(function() { key_resp.start(); }); // start on screen flip
psychoJS.window.callOnFlip(function() { key_resp.clearEvents(); });
}
if (key_resp.status === PsychoJS.Status.STARTED) {
let theseKeys = key_resp.getKeys({keyList: ['space'], waitRelease: false});
_key_resp_allKeys = _key_resp_allKeys.concat(theseKeys);
if (_key_resp_allKeys.length > 0) {
key_resp.keys = _key_resp_allKeys[_key_resp_allKeys.length - 1].name; // just the last key pressed
key_resp.rt = _key_resp_allKeys[_key_resp_allKeys.length - 1].rt;
// a response ends the routine
continueRoutine = false;
}
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of trialComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function trialRoutineEnd() {
return async function () {
//------Ending Routine 'trial'-------
for (const thisComponent of trialComponents) {
if (typeof thisComponent.setAutoDraw === 'function') {
thisComponent.setAutoDraw(false);
}
}
psychoJS.experiment.addData('key_resp.keys', key_resp.keys);
if (typeof key_resp.keys !== 'undefined') { // we had a response
psychoJS.experiment.addData('key_resp.rt', key_resp.rt);
routineTimer.reset();
}
key_resp.stop();
// the Routine "trial" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
return Scheduler.Event.NEXT;
};
}
var trials;
var currentLoop;
function trialsLoopBegin(trialsLoopScheduler, snapshot) {
return async function() {
TrialHandler.fromSnapshot(snapshot); // update internal variables (.thisN etc) of the loop
// set up handler to look after randomisation of conditions etc
trials = new TrialHandler({
psychoJS: psychoJS,
nReps: 5, method: TrialHandler.Method.RANDOM,
extraInfo: expInfo, originPath: undefined,
trialList: undefined,
seed: undefined, name: 'trials'
});
psychoJS.experiment.addLoop(trials); // add the loop to the experiment
currentLoop = trials; // we're now the current loop
// Schedule all the trials in the trialList:
for (const thisTrial of trials) {
const snapshot = trials.getSnapshot();
trialsLoopScheduler.add(importConditions(snapshot));
trialsLoopScheduler.add(StimuliRoutineBegin(snapshot));
trialsLoopScheduler.add(StimuliRoutineEachFrame());
trialsLoopScheduler.add(StimuliRoutineEnd());
trialsLoopScheduler.add(endLoopIteration(trialsLoopScheduler, snapshot));
}
return Scheduler.Event.NEXT;
}
}
async function trialsLoopEnd() {
psychoJS.experiment.removeLoop(trials);
return Scheduler.Event.NEXT;
}
var r;
var photo;
var value;
var delay;
var StimuliComponents;
function StimuliRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//------Prepare to start Routine 'Stimuli'-------
t = 0;
StimuliClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
r = Math.random();
if (((r < 0.2) && (flage === 0))) {
photo = "img/target.png";
value = 2;
flage = 1;
} else {
if ((r < 0.4)) {
photo = "img/nontarget1.png";
value = 1;
flage = 0;
} else {
if ((r < 0.6)) {
photo = "img/nontarget2.png";
value = 1;
flage = 0;
} else {
if ((r < 0.8)) {
photo = "img/nontarget3.png";
value = 1;
flage = 0;
} else {
photo = "img/nontarget4.png";
value = 1;
flage = 0;
}
}
}
}
delay = (1 + Math.random());
image.setImage(photo);
// keep track of which components have finished
StimuliComponents = [];
StimuliComponents.push(image);
StimuliComponents.push(eeg_marker);
for (const thisComponent of StimuliComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
function StimuliRoutineEachFrame() {
return async function () {
//------Loop for each frame of Routine 'Stimuli'-------
// get current time
t = StimuliClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *image* updates
if (t >= delay && image.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
image.tStart = t; // (not accounting for frame time here)
image.frameNStart = frameN; // exact frame index
image.setAutoDraw(true);
}
frameRemains = delay + 0.2 - psychoJS.window.monitorFramePeriod * 0.75; // most of one frame period left
if (image.status === PsychoJS.Status.STARTED && t >= frameRemains) {
image.setAutoDraw(false);
}
// eeg_marker updates
if (t >= delay && eeg_marker.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
eeg_marker.tStart = t; // (not accounting for frame time here)
eeg_marker.frameNStart = frameN; // exact frame index
psychoJS.window.callOnFlip(function() {
if (typeof emotiv != "undefined") {
emotiv.sendMarker(value.toString(), photo.toString(), false)
}
});
eeg_marker.status = PsychoJS.Status.STARTED;
}
frameRemains = delay + 1 - psychoJS.window.monitorFramePeriod * 0.75; // most of one frame period left
if (eeg_marker.status === PsychoJS.Status.STARTED && t >= frameRemains) {
eeg_marker.status = PsychoJS.Status.FINISHED;
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of StimuliComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function StimuliRoutineEnd() {
return async function () {
//------Ending Routine 'Stimuli'-------
for (const thisComponent of StimuliComponents) {
if (typeof thisComponent.setAutoDraw === 'function') {
thisComponent.setAutoDraw(false);
}
}
// the Routine "Stimuli" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
return Scheduler.Event.NEXT;
};
}
function endLoopIteration(scheduler, snapshot) {
// ------Prepare for next entry------
return async function () {
if (typeof snapshot !== 'undefined') {
// ------Check if user ended loop early------
if (snapshot.finished) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry(snapshot);
}
scheduler.stop();
} else {
const thisTrial = snapshot.getCurrentTrial();
if (typeof thisTrial === 'undefined' || !('isTrials' in thisTrial) || thisTrial.isTrials) {
psychoJS.experiment.nextEntry(snapshot);
}
}
return Scheduler.Event.NEXT;
}
};
}
function importConditions(currentLoop) {
return async function () {
psychoJS.importAttributes(currentLoop.getCurrentTrial());
return Scheduler.Event.NEXT;
};
}
async function quitPsychoJS(message, isCompleted) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry();
}
psychoJS.window.close();
psychoJS.quit({message: message, isCompleted: isCompleted});
return Scheduler.Event.QUIT;
}