PsychoPy Python To Javascript Crib Sheet
PsychoPy Python To Javascript Crib Sheet
5
Python to JavaScript Crib Sheet
Compiled by Wakefield Morys-Carter, Oxford Brookes University
Please share widely and follow Psych_Stats on twitter.
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
Python PsychoJS
2
Version 2020.1.3
thisExp thisExp=psychoJS.experiment;
win win=psychoJS.window;
webbrowser.open(“link”) webbrowser=window;
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.
3
Python to JavaScript Crib Sheet
code.
Fixed in 2020.2.5
https://discourse.psychopy.org/t/translation-
to-js-problem-in-stroopextended-demo-
adding-feedback/13446/4
pi pi=Math.PI;
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
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
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”
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
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.
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 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
psychoJS.downloadResources(stimuli);
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
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.
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])
9
Python to JavaScript Crib Sheet
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 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
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
Video Stimuli
Don’t Do
Use MPEG-4 encoding. Use H.264 encoded MP4 files with AAC
audio encoding. d.govan
11
Python to JavaScript Crib Sheet
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.
Python PsychoJS
None undefined
(the Auto translate incorrectly translates to
null)
'delimeter'.join(array) array.join(“delimeter”);
12
Version 2020.1.3
.upper() .toUpperCase()
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.
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.
Python Notes
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);
Error Messages
Cannot read property '0' of undefined
nReps for all loops should be numbers or a variable without the $.
15
Python to JavaScript Crib Sheet
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.
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
17
Python to JavaScript Crib Sheet
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.
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().
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
Each Frame
continueRoutine = continue_routine;
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)
});
jonathan.kominsky
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
21
Python to JavaScript Crib Sheet
text.contains
https://github.com/psychopy/psychojs/issues/55
Put a rectangle round the text and then use rec.contains
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
Music Box
● Plays music not by note from a spreadsheet
● Too memory intensive to use on a mobile device
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