Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
15K views

PsychoPy Python To Javascript Crib Sheet

This document provides a crib sheet for translating Python code used in PsychoPy experiments to JavaScript for online experiments. It includes tips for common issues like math functions and recommendations to add a "code_JS" component to the first routine for defining functions. The document is aimed at PsychoPy users and covers topics like data handling, stimuli, and code that requires manual translation.

Uploaded by

courursula
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15K views

PsychoPy Python To Javascript Crib Sheet

This document provides a crib sheet for translating Python code used in PsychoPy experiments to JavaScript for online experiments. It includes tips for common issues like math functions and recommendations to add a "code_JS" component to the first routine for defining functions. The document is aimed at PsychoPy users and covers topics like data handling, stimuli, and code that requires manual translation.

Uploaded by

courursula
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 23

PsychoPy version 2020.1.3-2020.2.

5
Python to JavaScript Crib Sheet
Compiled by Wakefield Morys-Carter, Oxford Brookes University
Please share widely and follow Psych_Stats on twitter.

PsychoPy 2020 has an automatic Python to Javascript translation tool.


This document contains information about code that is not translated correctly.
Please tweet @Psych_Stats to make suggestions for improvements and additions.

This document is primarily aimed at people who have used PsychoPy and have used Python
code snippets. For a simpler introduction, please refer to this quick start guide by Anastasia
Sares. If you use Sona Systems and/or Qualtrics, please refer to this guide by Lindsay
Santacroce on how to transfer ID variables from one platform to another.

Contents
1. code_JS -- an extra code component in the first routine
a. Maths functions
2. Common problems -- notes on working with Pavlovia
a. Gitlab functionality
b. Browser Compatibility
3. Coding notes -- dos and don’ts
a. Visual stimuli
b. Audio stimuli
c. Video stimuli
4. Translations that require manual editing -- when to override auto translation.
5. Colours -- a tip for working with colours as variable names.
6. Examples of working Python code
7. Error messages
8. Useful scripts
9. Not yet working
10. Links

code_JS
I strongly recommend that everyone adds a JavaScript only code component to their first
routine as follows

1. Create a code component in your first routine.


2. Rename it to code_JS
3. Switch it from Auto->JS to JS
4. Copy and paste code from the PsychoJS column below in Begin Experiment (not
Before Experiment). The elements in bold are ones I find particularly useful.
Python to JavaScript Crib Sheet

Python PsychoJS

2
Version 2020.1.3

thisExp thisExp=psychoJS.experiment;

win win=psychoJS.window;

event, e.g. for event.getKeys() or event=psychoJS.eventManager;


event.clearEvents()

shuffle() shuffle = util.shuffle;

.append dvbridges Array.prototype.append = [].push;

sort() sort = function(array) {


return array.sort();
}
or for a numerical sort
sort = function(array) {
return array.sort((a, b) => (a - b));
}

.count() Rebecca Hirst Array.prototype.count = function(value) {


let count = 0;
this.forEach(item => {
Usage if (item === value) {
count++;
totalScore = score.count(1) would count the }
number of times 1 appears in an array });
called score. return count;
}

webbrowser.open(“link”) webbrowser=window;

This also requires import webbrowser in a


Python only code block.

Multi-line text is centre aligned locally and left aligned online. To centre align a component
called text_2, add text_2.setAlignHoriz('center'); to a Begin Routine tab of code_JS.

Maths functions
Maths functions sometimes seem to auto translate correctly. For when they don’t, it is
possible to define the functions or aliases at the start rather than replacing every time.

random() random = Math.random;

randint(min,maxplusone) randint = function(min, maxplusone) {


return Math.floor(Math.random() *
The auto translate in 2020.1 produces a (maxplusone - min) ) + min;
range which includes the max value. To }
suppress it write
0+randint(min,maxplusone) in your Python

3
Python to JavaScript Crib Sheet

code.

range() aisa2 range = function (size, startAt = 0) {


return [...Array(size).keys()].map(i => i +
startAt);
}

sum() sum = function (arr) {


return arr.reduce((a,b)=>a+b)
Write sum()+0 if you need to suppress the }
auto translate. OR
sum = (arr) => arr.reduce((a,b)=>a+b)

np.average() average = (arr) => (arr.reduce((a, b) => (a +


b), 0)) / arr.length

Fixed in 2020.2.5

round(a,b) round = function(num, n=0) {


return +(Math.round(num + ("e+" + n)) +
a = number to round ("e-" + n));
b = number of digits }

https://discourse.psychopy.org/t/translation-
to-js-problem-in-stroopextended-demo-
adding-feedback/13446/4

Fixed in 2020.2 by the addition of the line


const { abs, sin, cos, PI: pi, sqrt } = Math;

abs() abs = Math.abs;

sin() sin = Math.sin;

cos() cos = Math.cos;

pi pi=Math.PI;

sqrt() sqrt = Math.sqrt;

Common problems
Mac version 2020.1.3 shows up as 2020.1.2. Don’t worry about this.

If images, etc., aren’t getting uploaded automatically, copy them to the local
html/resources folder and they should get uploaded at the next sync. PsychoPy tries to

4
Version 2020.1.3

copy necessary resources to this folder but is unable to detect resources specified in
code components. https://discourse.psychopy.org/t/reading-list-index-in-sound-
component/13142/7

Use Developer Tools (Ctl-Shift-I in Windows/Chrome, Cmd-Opt-J in Mac/Chrome, F12 in


IE/Edge, Ctrl-Shift-J in Windows/Safari, Ctrl-Opt-J in Mac/Safari ) to view errors via the
browser console if you aren’t getting sufficient information from PsychoPy. You can add
print(var) (which translates to console.log(var); ) to check the value of a variable var at a
particular point. N.B. There’s nothing to stop your participants doing this which may give
them access to trial variables you may be loading from Excel and allow cheating on
experiments with a performance based reward.

If Pavlovia refuses to run the latest upload of your experiment, disable the cache via
Network conditions in the browser console. You may also need to clear the cache when
you first set this (using Ctl-F5, Ctl-Shift-R or equivalent). When you have developer tools
showing you can also press and hold the page refresh icon and select Empty Cache and
Hard Reload.

Since Pilot tokens expire, the easiest way to demo your experiment is to set it to
RUNNING and allocate it a small number of credits, but edit the final screen so that it
can’t finish (possibly unless you press a key such as 0 which isn’t typically used). You
can then set your experiment not to save partial data using the Dashboard entry for your
project. That way, if the experiment fails to finish, no data is saved and therefore no
credit is consumed. If you wish to view pilot data files, the saving format must be CSV
not DATABASE and you may need to set the experiment to PILOTING.

If you have access to questionnaire software, I would recommend that you use that for
your PI sheet, ID number and demographics. Your final PsychoPy routine should include
words along the lines of “Please wait for the message “ Thank you for your patience” to
appear and then click OK to continue the experiment”. The “Completed URL” can be
formatted like a text element to include variables, e.g.
$"https://brookeshls.co1.qualtrics.com/surveyname?
expname="+expName+"&participant="+expInfo['participant']. In Pavlovia expName is
the file name for the PsychoPy file used to create the experiment. So long as you
have URLs set in the experiment options, it may be possible to change them using
the following command: PsychoJS.setRedirectUrls(myCompletedURL, myCancelledURL)
https://discourse.psychopy.org/t/completedurl-incompleteurl/5597/23?u=wakecarter

If you accidentally corrupt your experiment by removing all variables from the expInfo
section, try adding <Param name="Experiment info" updates="None"
val="{'participant': ''}" valType="code"/> to the Settings section of your
Builder .psyexp file in a text editor.

5
Python to JavaScript Crib Sheet

Add event.clearEvents() to Begin Routine to clear key presses from previous


routines. For mouse input, record the location of the mouse and pass if it hasn’t
changed, e.g.
Begin Routine
mouserec = mouse.getPos()

Each Frame
mouseloc = mouse.getPos()
if mouseloc[0]==mouserec[0] and mouseloc[1]==mouserec[1]:
Pass
elif …

Gitlab Functionality
The change permissions: “view code”, then “settings -> general -> permissions”

To copy someone else’s experiment:#


1. Go to their gitlab page.
2. Click on “fork” and choose your own name.
3. Open PsychoPy select “find existing project online”.
4. Select the desired folder.
5. Select the desired project online and press sync.
6. Open the newly downloaded psyexp file in PsychoPy.

By default, only the project maintainer can update the code by syncing with PsychoPy. To
allow Developers to make changes, the Maintainer should go to Settings - Repository -
Protected Branches and allow Developers and Maintainers to push and merge.

If your experiment won’t sync to Gitlab, the safest way to create a new experiment on Gitlab
is to duplicate your local experiment folder and then delete the hidden folder called .git from
inside it. Then open the duplicate study in PsychoPy and sync as before so that it will ask to
create a new project. [jon]

Browser Compatibility
For example, some people have reported keyboard responses causing a beep in
Safari on Macs. I’m unsure whether this is for keyboard components, event.getKeys
and/or core.Keyboard . https://discourse.psychopy.org/t/safari-annoying-beep/8746?
u=wakecarter

Coding notes
Don’t Do
Have anything showing in “Use PsychoPy This option should be used if you have a
Version” unless you have a good reason. working experiment which you don’t want to

6
Version 2020.1.3

accidentally break when you upgrade.

Use import Remove all references to avoid the


experiment crashing while “initialising the
You cannot use pandas, numpy, etc. experiment”.

Change or remove “participant” from the Add ?participant=x or &participant=x to the


Experiment Info fields. URL if you want to bypass the Experiment
Info fields, for example if you want to assign
a participant number from Qualtrics.

Use simple terms for variables, since they Use variable names that make sense.
may already be in use by PsychoPy.
Variable names to avoid are: core, Object,
Length, Number, sound, Symbol, t, thisTrial,
trials.

End a routine using the same keypress or Add an ISI or record the current mouse
mouse click as the previous routine without location / key press at the start of the
doing something to stop the existing routine and then only accept responses if
response being recorded again. the location changes or there has been a
period of no response first.

e.g (for touch screen)


mouseloc = mouse.getPos()
if mouseloc[0]==mouserec[0] and
mouseloc[1]==mouserec[1]:
pass
elif t<2:
mouserec = mouseloc
elif rec.contains(mouse):
continueRoutine = False

Have a final routine where you have to The final routine either needs components
press escape to end the experiment. with a duration or a keyboard/mouse
component that is set to end the routine on
a valid response.

Use $slice(0,10) for Selected rows in a loop. Use 0:10 instead, for the first 10 items. Note
that a:b will use rows (a+2) to (b+1)
inclusive.

Use functions for component parameters, Define the value you want to use as a
including variable components. variable in a code component (so it can be
auto translated) and then use the variable
name in the component itself.

Use Python string formatting “my var = %s” Use string concatenation with type
% myVar conversion “my var = “ + str(myVar)

7
Python to JavaScript Crib Sheet

Use str(int(myVar)) if you are getting .0


added to values that should be integers.

Use RatingScales or Forms Use Slider instead, which has simpler code.
https://discourse.psychopy.org/t/forms-on-
pavlovia/13512/10

Create slider components in code. Use a slider component in Builder and then
awong26 modify in code
using .size, .labels, .ticks, ._needVertexUpd
ate, and ._buildSlider()

Use Pandas for reading Excel files Load variable information into arrays or
dictionaries via a loop prior to the trials loop.
For example:
Initialise a variable in Begin Experiment
Append values to that variable in Begin
Routine, e.g.
wordList.append([Item,Valence,Answer])
See my template experiment here:
Experiment | Wake/brookes-template-2020

myData = data.TrialHandler(nReps=1, myData = new TrialHandler({


method='sequential', extraInfo=expInfo, psychoJS: psychoJS,
originPath=-1, nReps: 1, method:
trialList=data.importConditions('conditions.xl TrialHandler.Method.SEQUENTIAL,
sx'), seed=None, name='myData') extraInfo: expInfo, originPath: undefined,
trialList: 'conditions.xlsx',
Access individual values using: aValue = seed: undefined, name: 'myData'});
myData.trialList[Idx]['variableName']

Use + for appending arrays. Use .append(), which will be translated


hellenjingyuan to .push() by code_JS

Use random.seed() No seed is necessary for pseudo random


numbers. However, if you want to use a
repeatable set of random numbers or
improve on the standard seed, please look
at the useful scripts section.

Don’t call psychoJS.downloadResources Create an array of dictionaries, e.g.


multiple times to download resources. stimuli=[];
stimuli.push({name:(filename),path:
(filenameWithPath)});

psychoJS.downloadResources(stimuli);

if (mouse.getPressed[0] == 1) if (mouse.getPressed()[0] == 1) dvbridges

Use \ in file paths. Use / instead.

Have a [ ] brackets in your Excel file, unless Use a different symbol, or potentially use a
the field is intended to be an array. code for the bracket instead (not tested).

8
Version 2020.1.3

This is a new error in 2020.2.

Have a single allowed key in a keyboard Use brackets or a comma to show that the
component, e.g. ‘space’ allowed keys are a list, e.g.
This is a new error in 2020.2. [‘space’] or ‘space’,’space’

Visual Stimuli

Don’t Do
Use win.flip() Just don’t (in Python or JavaScript) if you
are using Builder. There is an implicit
Use event.waitKeys() win.flip() at the end of the Each Frame tab
and additional flips or halting the process
Use while with waitKeys or while will confuse the
Builder code.

Skip the file extension. Locally PsychoPy will check for known file
types, so image01 will find image01.png.
Online the file extension must be specified.

Use tiff or bmp file formats for images Use jpg or png. For png images, do not
save a colour profile. kevinhroberts

Use .draw if you want an object to appear Create the object in Begin Experiment.
for the whole routine. Replace with .setAutoDraw(True) in Begin
Routine and .setAutoDraw(False) in End
Routine, which translate to Javascript
correctly.

Using .draw in Each Frame is fine if you


only want the object to appear during a
subset of frames.

Use “Set every frame” in builder Make edits to existing stimuli in Each
components. Frame code components, e.g.
ccimage.size=[x_size*x_scale,
y_size*y_scale] or
polydart.setPos([dartx,darty])

Use text.setText(msg) Use text.text=msg


The .set method seems to work in most
cases but .setText appears to be an
exception.

Use text.setOpacity() by itself Add a line text.text= straight after the

9
Python to JavaScript Crib Sheet

https://github.com/psychopy/psychopy/ opacity line or avoid changing the opacity of


issues/1045 text stimuli by hiding with a polygon of the
background colour instead. This is only
needed for text stimuli.

Use continueRoutine = False in Begin Either move the code to Each Frame or use
Routine a different method for skipping the routine
such as having a loop around it with nReps
= 0 or setting the duration of the
components to 0.

Update the display every frame if nothing Compare the new frame with the old frame
has changed. This can cause memory and only change objects if there is a
errors. difference.

Use fill colour $None Fill with background colour and place above
https://github.com/psychopy/psychojs/ anything that should appear inside it.
issues/72 Alternatively, add
polygon.setFillColor(undefined) to Begin
Routine

Rely on the order of the components to If you are making an edit to an object in the
determine order of being drawn. It seems as background, use .setAutoDraw(False)
though when an object gets edited it is followed by .setAutoDraw(True) on the
object(s) you want it to remain behind.
moved to the front.

Use an array in an Excel file for locations or Use separate variables for x and y
colours. coordinates, e.g. $(x,y) or separate r, g, b
values for separate colour values.

Use deg or cm for units Use “height” where possible. Norm and pix
also work. If you would like to add a routine
to allow the participant to resize an image of
a credit card to calculate their screen size,
you are welcome to use my ScreenScale
experiment.

Use visual.Circle, visual.Line or Use visual.Polygon or visual.ShapeStim


visual.ButtonStim. instead.

Have a text component set to nothing as Set it to a space character instead.


constant. This is a new error in 2020.2
and gives SyntaxError: Unexpected
token ',' in the console. The JavaScript
will look like: text: /* */
,

Use black text if you want participants to Use light text on a dark background.
use a mobile device. The background

10
Version 2020.1.3

colour seems to fail on some devices,


defaulting to black. Black text will therefore
be invisible.

Audio Stimuli

Don’t Do
Use .ogg Use .mp3 for best cross-browser
compatibility and revert to .ogg in Python.
For more details of audio format
compatibility, see
https://en.wikipedia.org/wiki/HTML5_audio#
Supported_audio_coding_formats

Use alphabetical notes (e.g. “A”). Numerical frequencies might work.


A = 220, 440, 880
“Middle” C = 262.63

The formula for notes on a piano is:


frequency = 440*2n/12
for n =-21 to 27

Use Stop durations Sounds should either be edited to correct


length or use sound.stop() in a code
component.

Try to create sound objects in code Create a sound component in an earlier


routine and set the start time to after you
.setSound() does not work want the routine to end. You may therefore
need to end the routine using
continueRoutine=False
.setVolume() does not work
You can then start and stop the sound in
code using .play() and .stop()
https://discourse.psychopy.org/t/
asynchronous-sound-success-story/13789

Video Stimuli

Don’t Do
Use MPEG-4 encoding. Use H.264 encoded MP4 files with AAC
audio encoding. d.govan

Use .reset() Use .stop() followed by .seek(0) wakecarter

11
Python to JavaScript Crib Sheet

Start a movie at 0 seconds. Set a start time to 1 second if the movie


This is a new error in 2020.2. refuses to display in full screen. Becca

Fixed in 2020.2

Don’t Do
Refer to custom loop names when using All loops are referred to as “trials”.
methods .thisN, .finished, etc. Use the custom loop name when referring
to trialList. Use “trials” for other attributes.
N.B. As of 2020.2 you can also also use
`currentLoop.finished` when you don't know If you only have one loop that you need to
the name of the loop that will be running refer to, rename it to “trials”. If you have
(e.g. when the Routine is inserted into several then don’t worry about renaming
multiple locations). them.

N.B. .thisN has stopped working in For nested loops the inner loop is
2020.2 but should be fixed in 2020.2.5 referenced. It may not be possible to
reference the outer loop.
https://discourse.psychopy.org/t/loop-
finished-true-no-longer-working/11190

Use a $ when specifying a variable for If the $ is shown in the PsychoPy dialogue
nReps box it probably isn’t needed locally in
PsychoPy but causes an error online.

Translations that require manual editing


Set Code Type to Both. N.B. See How to set Default Code Type to avoid frustration. I would
also recommend having code that needs to be manually edited in a separate code
component so that you can continue to use the auto translate for new code. Default Code
Type changes the whole component, not just the current tab.

Python PsychoJS
None undefined
(the Auto translate incorrectly translates to
null)

.split() .split(" "); kdoelling

'delimeter'.join(array) array.join(“delimeter”);

.pop(0) i.e. taking the first item in the list .shift()


instead of the last.

12
Version 2020.1.3

.insert(0,x) i.e. adding x to the start of a list .unshift(x); JensBoelte

import math Math.floor();


math.floor() N.B. Delete import statement

core.Clock new util.Clock;

.upper() .toUpperCase()

I haven’t yet successfully used:


String.prototype.upper =
““.toUpperCase; in code_JS

kb = keyboard.Keyboard() kb = new core.Keyboard({psychoJS:


dvbridges psychoJS, clock: new util.Clock(),
waitForStart: true});
Has anyone got this working for them? I
don’t get errors but I don’t get a working
keyboard either.

core.quit() psychoJS.quit({message: 'Your custom


message’}); OR
quitPsychoJS('Your custom message',
false);
More testing needed.

You should also be able to add to the


standard message by setting the variable in
PsychoPy, e.g. message = “Please click ‘ok’
to be debriefed.”; in the End Experiment tab
of code_JS

Using multiplication to create a larger array, Array(10).fill([0,1]).flat(); dvbridges


e.g. [0, 1] * 10

Counting down, e.g. for (var Idx = 10, _pj_a = 0; (Idx > _pj_a);
for Idx in range(10,0,-1): Idx += (- 1)) {
Auto->JS fails to switch < to > in the ending
condition.

for i, j in enumerate(distList['list']): for ([i, j] of dictList['list'].entries()) dvbridges

thisExp.addData('Typed response', thisExp.addData('Typed response',


textbox.text); textbox._pixi.text); sotiri
When saving responses from editable
text boxes.

Colours
Named colours seem to cause issues with the auto translate. The easiest way to get around
this is to define colours as variables in a code_Both component in the first routine. This

13
Python to JavaScript Crib Sheet

table contains some examples. The RGB values range from -1 to +1 so divide by 127.5 and
subtract 1 if you have 0-255 values (or divide by 50 and subtract 1 if you have percentages).

N.B. This method cannot be used to set text colour within a text component, but can be use
for text.color= or rec.setFillColor()

Python PsychoJS
white=[1,1,1] white = new util.Color([1, 1, 1]);
grey=[.2,.2,.2] grey = new util.Color([.2, .2, .2]);
yellow=[1,1,0] yellow = new util.Color([1, 1, 0]);
green=[-1,0,-1] green = new util.Color([-1, 0, -1]);
black=[-1,-1,-1] black = new util.Color([-1, -1, -1]);
red=[1,0,0] red = new util.Color([1, 0, 0]);

This method can also be used for defining colours from a spreadsheet. I’m not yet sure if
the spreadsheet can contain a single column, e.g. Colour, containing arrays such as [1,-
1,.2] or whether separate columns for Red, Green and Blue are needed.

thisColour=Colour thisColour = new util.Color(Colour);

Examples of working Python code

Python Notes

target = visual.Polygon( This code must be in Begin Experiment.


win=win, See above for how to define win and
name="target", colours.
fillColor=yellow,
Display using target.setAutoDraw(True) in
lineColor=white, Begin Routine.
edges=36,
pos = [0,voffset], Hide using target.setAutoDraw(False) in
size=scale*dtarget End Routine.
)
For the loop, edit append to push in the
polydart=visual.ShapeStim( Javascript
win=win, name="dart",
fillColor=blue, To control changes in colour use, e.g.
lineColor=white, target.setFillColor(red)
vertices=[[0,.5],[.01,.48],[.01,.28],[.08,.15], polydart.setLineColor(yellow)
[.05,-.25],[0,-.5],[-.05,-.25],[-.08,.15], polydart.setLineWidth(5)
[-.01,.28],[-.01,.48]],
pos = dartoldpos
)

errortext = visual.TextStim(win=win,

14
Version 2020.1.3

name='errortext',
text='Throw',
font='Arial',
pos=(0.3, .45), height=0.08,
wrapWidth=None, ori=0,
color=white);

for Idx in range (9):


bars.append(visual.ImageStim(
win=win,
name='ladder',
image='ladder.png', ori=0,
pos=[pointsz[Idx]*scale*dtarget/2,(Idx-
4)*scale+voffset],
size=[scale*dtarget*28.72,scale*dtarget],
color=white, colorSpace='rgb', opacity=1,
flipHoriz=False, flipVert=False,
texRes=128, interpolate=True)
)

psychoJS.downloadResources([ This will download custom files from outside


{ name: (expInfo["participant"] + "s.jpg"), the html folder based on the participant
path: ("../faces/" + expInfo["participant"] + number.
"s.jpg") },
{ name: (expInfo["participant"] + ".jpg"), Make sure there is time for the files to be
path: ("../faces/" + expInfo["participant"] + downloaded before they need to be used.
".jpg") }
]); Address the resources in the experiment
without their file path.

Error Messages
Cannot read property '0' of undefined
nReps for all loops should be numbers or a variable without the $.

CONTEXT_LOST_WEBGL: loseContext: context lost


This indicates a memory overload. Ensure that updates don’t happen on frames where
nothing has changed. Also, add if (typeof this._pixi !== 'undefined')
this._pixi.destroy(true); before the creation of a new stimulus called this.
frank.papenmeier

Could not find an appropriate player.


If you are downloading sound files on the fly using psychoJS.downloadResources
then this error suggests that you haven’t waited long enough for the file to be
downloaded.

15
Python to JavaScript Crib Sheet

Illegal return statement


Unbalanced {} brackets in JS code

No default wrap width for unit: undefined


Safari may fail to register the global units setting. If this is the case, select the units
setting manually for each component. https://discourse.psychopy.org/t/safari-text-
stim-error-no-default-wrap-width-for-unit-undefined-with-workaround/13482

ReferenceError: Can’t find variable: function_name


Define function_name in your initial JavaScript code block

TypeError: Cannot assign to read only property ‘undefined’ of object '#'


You may have blank columns in one of your conditions files [dvbridges] e.g.,
https://discourse.psychopy.org/t/unknown-resource-conditions-file/13700/2

TypeError: x is not a constructor


These methods do not work online. Use an alternative.

ReferenceError: frameDur is not defined


frameDur is a PsychoJS variable used for calculating frame durations. This error
tends to occur when the JS has not compiled correctly, and so PsychoPy fails to
declare the builtin variables in the JS code. The actual error is usually visible from
the Psychopy dialog.
https://discourse.psychopy.org/t/referenceerror-framedur-is-not-defined-problem-
converting-from-pyschopy-to-js/13270

ReferenceError: variable_name is not defined


Set a value for variable_name in a Begin Experiment code block.

TypeError: Cannot read property ‘map’ of undefined


If you are checking the mouse location, for example with “if rec.contains(mouse):”,
ensure that the component for the object to be checked is above the code
component that checks it.

TypeError: Cannot read property x of undefined


The issue is likely to be with the variable or array or variable for which you are trying
to find property x, rather than any issue with finding property x in general.

Unknown Resource
when setting the image of ImageStim: [component name]
when getting the value of resource: [resource name including path and extension]

16
Version 2020.1.3

unknown resource
Copy any missing resources from the experiment folder to html/resources and sync
again.

Unable to create link to PsychoJS library folder


Have you edited your .gitignore file? You can retrieve the default contents from
https://github.com/psychopy/psychopy/blob/master/psychopy/projects/gitignore.py
lib folders used for local debugging can clash with online libraries.
https://discourse.psychopy.org/t/unable-to-create-link-to-psychojs-library-folder/
12074

Unable to get property ‘getKeys’ of undefined or null reference


event needs to be defined for event.getKeys() to work, i.e.
event=psychoJS.eventManager;

Unspecified JavaScript error


This error is very difficult to debug, but I have come across two causes, a text
element with a decimal size (e.g. .1) when the units are in pixels and setting an
image as unspecified. Please add comments for any other causes of these errors.

This file doesn’t belong to any existing project


1. Install Git from https://git-scm.com/download/
2. Opening a command prompt (‘cmd’).
3. Navigate to the folder where all of your files are (using ‘cd’), e.g. cd desktop\
experiment. Now you can start pushing things to pavlovia manually.
4. Type ‘git status’ (check to see if files have changed, only looks at local directory)
5. Type ‘git add -A’ (add changes to staging area, ready to send online)|
6. Type ‘git commit -m “(insert message here)”’ (commit the changes and add a
message detailing the contents of the commit)|
7. Type ‘git pull’ (you will have to make sure that the any new files (primarily data
output files), in the online repository are first added to your local repository,
otherwise git will be angry)|
8. Type ‘git push’ (actually send the commits online)|
9. Type ‘git log’ (check the log of commits to make sure it worked, press q to exit if
necessary)|
10. Type ‘git status’ (double check to make sure staging area is clear and no further
updates are necessary)

Useful scripts
Assign Consecutive Participant IDs
I have written a web app. which can be used for Pavlovia experiments:
https://moryscarter.com/vespr/pavlovia.php

The page takes four parameters: folder, experiment, id and researcher.

17
Python to JavaScript Crib Sheet

folder (required) is your Pavlovia username


experiment (required) is your experiment name as formatted for the study link. If your
recruitment URL does not contain /html/ then add a / to the end of your experiment name.
id (optional) is a unique code which you could assign, for example as a random number in
Qualtrics, so you can link your PsychoPy data back. It is passed to Pavlovia unchanged
researcher (optional) is also passed to Pavlovia unchanged. I am using it to ensure that
participants start in Qualtrics where they can view the participant information sheet, not
Pavlovia.
participant is assigned by my app. as a consecutive number. You could use a modulus of
participant in order to assign participants to conditions. The number increments whether or
not the participant finishes. It would be considerably more complicated to re-assign aborted
participant numbers.

Example URL: https://moryscarter.com/vespr/pavlovia.php?


folder=Wake&experiment=prospective-memory-ldt&researcher=wakecarter&id=test

In PsychoPy if you have condition = int(expInfo['participant'])%3 then condition should


be assigned values 0, 1 or 2.

See also https://moryscarter.com/vespr/survey.php which has similar functionality.

This code isn't open source, but anyone can use my app. for their own experiment. When using to
redirect there is no branding and I could easily tailor it if requested, including to redirect to
somewhere other than Pavlovia.

Seed the Random Number Generator


Solution from
http://davidbau.com/archives/2010/01/30/random_seeds_coded_hints_and_quintillions.html

Add the following code to code_JS.


N.B. This code is “minified”. The full version (and any updates of this version) come from
https://github.com/davidbau/seedrandom

!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||


(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=s&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a)
{for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=s&f+1],c=c*d+h[s&(h[f]=h[g=s&g+b])+
(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&"object"==e)for(c
in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:"string"==e?a:a+"\0"}function l(a,b)
{for(var c,d=a+"",e=0;e<d.length;)b[s&e]=s&(c^=19*b[s&e])+d.charCodeAt(e++);return
n(b)}function m(c){try{return o?n(o.randomBytes(d)):(a.crypto.getRandomValues(c=new
Uint8Array(d)),n(c))}catch(e){return[+new Date,a,
(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return
String.fromCharCode.apply(0,a)}var o,p=c.pow(d,e),q=c.pow(2,f),r=2*q,s=d-
1,t=c["seed"+i]=function(a,f,g){var h=[];f=1==f?{entropy:!0}:f||{};var o=l(k(f.entropy?
[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(f.pass||g||function(a,b,d){return d?

18
Version 2020.1.3

(c[i]=a,b):a})(function(){for(var
a=s.g(e),b=p,c=0;q>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=r;)a/=2,b/=2,c>>>=1;return(a+c)/
b},o,"global"in f?f.global:this==c)};if(l(c[i](),b),g&&g.exports)
{g.exports=t;try{o=require("crypto")}catch(u){}}else h&&h.amd&&h(function(){return t})}(this,
[],Math,256,6,52,"object"==typeof module&&module,"function"==typeof
define&&define,"random");

You can then seed the random numbers by adding Math.seedrandom("hello."); to code_JS.

If you use the seed “hello.” then the first two numbers generated by random() will be
0.9282578795792454 and then 0.3752569768646784. seedString = Math.seedrandom();
will seed based on current time, dom state, and other accumulated local entropy. The seed
generated is saved to seedString.

If you want to create random numbers with a custom seed without affecting Math.random,
add var myrng = new Math.seedrandom('hello.'); to code_JS and then retrieve random
numbers using myrng().

Embed html content (e.g. forms and media)


Create a JS only code component as follows. The html file referenced in the first line should
be uploaded to the html folder of your experiment. You should also add another element
(e.g. text “Loading..”) with no duration if you want the routine to wait for a form in the html file
to be submitted. Variables from the form are saved to the data file.

For more details see: Custom web component for online experiments: Forms, surveys,
questionnaires, and other web-based content

Begin Routine
let src = 'yourcustomcontent.html';
continue_routine = true; // Routines can't be ended from within Begin Routine
$(document).ready(function() {
// Add custom contents from html file using an iframe:
$('body').append('<div id="iframe-o" style="visibility: hidden; position: relative; display:
table; margin: auto;"><div id="iframe-m" style="display: table-cell; vertical-align:
middle;"><div id="iframe-i" style="display: inline-block; width:100%; overflow-y: auto;
overflow-x: hidden;"><iframe id="iframe" src="'+src+'" style="width:
100%"></iframe></div></div></div>');

$('#iframe').on('load',function(iframe){
// Auto-adjust iframe size:
$(this).contents().find('html').css({ 'display': 'table', 'width': '100%', 'overflow-x':
'hidden' });
$('#iframe-o').height($(window).height()-20, true);
$('#iframe-m').width($(this).contents().find('html').width()+20);

19
Python to JavaScript Crib Sheet

$('#iframe-i').height ( Math.min ( $(this).contents().find('html').height()+20, $


(window).height()-20 ), true );
$(this).height($(this).contents().find('html').height());
$('#iframe-o').css('visibility','visible');

// If iframe contains a form, then capture its output text:


$(this).contents().find('form').on('submit',function(e){
e.preventDefault();
$.each($(this).serializeArray(),function(i, param){
params[param.name] = param.value;
psychoJS.experiment.addData(param.name, param.value);
});
console.log ( 'DEBUG:FRM', params );
// Remove iframe and continue to next routine when done:
$('#iframe-o').remove();
continue_routine = false;
});
});
});
//$('#iframe').attr( 'src', function(i,val){ return val;} );

Each Frame
continueRoutine = continue_routine;

Save Screen Information


var sUsrAg;
var nIdx;
function getBrowserId () {
var browsers = ["MSIE", "Firefox", "Safari", "Chrome", "Opera"];
sUsrAg = window.navigator.userAgent,
nIdx = browsers.length - 1;
for (nIdx; nIdx > -1 && sUsrAg.indexOf(browsers [nIdx]) === -1; nIdx--);

return browsers[nIdx];
}

expInfo['OS'] = window.navigator.platform;
expInfo['browser'] = getBrowserId();
expInfo['xResolution'] = screen.width;
expInfo['yResolution'] = screen.height;

Save IP address
$.getJSON('https://api.ipify.org?format=json', function(data){

20
Version 2020.1.3

console.log(data.ip);
localStorage.setItem('ip',data.ip);

psychoJS.experiment.addData('IP_Addresss', data.ip)

});

Automatically exit if the experiment is running on a mobile


device
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera
Mini/i.test(navigator.userAgent) ) {
quitPsychoJS('Mobile device detected. Goodbye!', false)
}

Hide the mouse cursor


document.body.style.cursor='none';
You need to add the following code when you want to un-hide it:
document.body.style.cursor='auto';

jonathan.kominsky

Disable default behaviour of right mouse click (so it can be


used by PsychoPy)
preventClick = function (event) { event.preventDefault(); }
document.addEventListener("contextmenu", preventClick, event, false);

To restore the default behaviour of the right mouse button:

document.removeEventListener("contextmenu", preventClick, event, false);

Summarise Responses
https://discourse.psychopy.org/t/accessing-experiment-data-via-code-component-for-
psychojs/8485/2

// Get JS array of trial objects (i.e., a list of python dicts with response data)
dat = psychoJS.experiment._trialsData

// Filter data to get correct trials


corr = dat.filter((trial) => trial['key_resp.corr'] === 1)

// Get RTs only as an array


rts = corr.map((trial) => trial['key_resp.rt'])

21
Python to JavaScript Crib Sheet

// Reduce RTs to a single number : the mean


meanRT = rts.reduce((a, b) => a + b) / rts.length

Not yet possible


Custom filenames and paths
https://discourse.psychopy.org/t/how-to-change-the-name-of-the-data-file-when-running-
online-experiment/5247

Forms and rating scales


https://discourse.psychopy.org/t/forms-on-pavlovia/13512/6?u=wakecarter

text.contains
https://github.com/psychopy/psychojs/issues/55
Put a rectangle round the text and then use rec.contains

Running PsychoJS experiments on a local server


https://github.com/psychopy/psychojs/issues/79

Control mouse cursor position


https://github.com/psychopy/psychojs/issues/35

22
Version 2020.1.3

Links
Github issues
PsychoPy discourse / Online experiments
Vertical Enhancement of Statistics and Psychology Research (resources page)
Wakefield Morys-Carter CV

Public PsychoPy Experiments (fork at will)


Brookes Template 2020
● Embedded form
● Seed Random
● Embedded YouTube video
● Slider
● Pre-loading items into an array
● Repeating incorrect items
● Inclusive gender options
● Text entry for age

Music Box
● Plays music not by note from a spreadsheet
● Too memory intensive to use on a mobile device

Prospective Memory LDT


● Pre-loading items into an array
● Interleaving items and targets
● Touchscreen + keyboard responses

Screen Scale
● Asks a participant to resize an image to the size of a physical credit card
● Use the results as vertical and horizontal scaling factors for norm or height units

23

You might also like