Virtual-chinrest, implementation or something else to harmonize visual angles?

URL of experiment:

Description of the problem:
I have looked through a few methods to resize images making them more similar in size across different monitors. I have found virtual_chinrest which I managed to clone and run, but I have not found a way to incorporate it into the start of my experiment nor how to extract the info.
I have also found this side which should be able to pair with my pavlovia EasyEyes | Remote Calibrator However this solution is stuck at “Retrieving experiment …”

I feel that my problem is not so unique and I wonder if there is some generalized solutions that I have overlooked. Otherwise, I will be very happy with all help in how to get the info from virtual_chinrest implemented in my linked paradigm. My main issue may be how to merge JS code with the python code I have used to define my current paradigm.

Best, Simon

I think there are two possible paths forward, but I can still not figure out neither

One is to inspect the variable ‘data’ as proposed in the documentation for virtual_chinrest. However, when I press F12 I still can’t find this. I can only find the following in the console as is printed by line 108:


It seems like the data for instance contains “viewDistance_mm” which seems very useful, but I cannot access this, let alone use it for informing my previous experiment. Edit, I changed the script to include console.log(data);

This outputs:

Variable Value
avgBallPos 332
ballPosition [335, 332, 330, 336, 329]
ballX 477
cardWidthPx 378
dataType configurationData
fullScreenClicked false
px2mm 4.42
rectX 795
sliderClicked true
squarePosition 760
viewDistance_mm 410

Another option, I think is to try to merge the two scripts virtual_chinrest and my ImagesSopaMulti in psychopy’s coder as I can find them both in JS there. After all line 125 of virtual_chinrest specifies.

        // You can then DO SOMETHING HERE TO PROCEED TO YOUR NEXT STEPS OF THE EXPERIMENT. For example, add a button to go to the next page.

But I am not at all sure how I can progress from that to an experiment I have made in the builder. If anyone here has created a script that does something similar, that is starting with virtual_chinrest and continuing with a custom program, I would be very interested to see it. Other than that, I would be happy with any input that may help me forward.

Still working on it, still new bugs every time I reupload and try to run it.

I have tried to split the code into several parts, basically taking virtual_chinrest and posting the main part of it in a code block to begin before the experiment

// Add the new properties to the existing “data” variable
data.dataType = “configurationData”;
data.ballPosition = ;
data.fullScreenClicked = false;
data.sliderClicked = false;

// Define a distanceSetup object
const distanceSetup = {};
distanceSetup.round = function (value, decimals) {
return Number(Math.round(value + “e” + decimals) + “e-” + decimals);
distanceSetup.px2mm = function (cardImageWidth) {
const cardWidth = 85.6; //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
const px2mm = cardImageWidth / cardWidth;
data.px2mm = distanceSetup.round(px2mm, 2);
return px2mm;

// Function to configure blind spot
function configureBlindSpot() {
$(“#blind-spot”).css({ visibility: “visible” });
$(document).on(“keydown”, recordPosition);

// Function to draw the ball animation
function drawBall(pos = 180) {
const mySVG = SVG(“svgDiv”);
const cardWidthPx = getCardWidth();
const rectX = distanceSetup.px2mm(cardWidthPx) * pos;
const ballX = rectX * 0.6;

let ball =, 50).fill(“#f00”);
window.ball = ball;

let square = mySVG.rect(30, 30).move(Math.min(rectX - 50, 950), 50);

// Save square position data
data.squarePosition = distanceSetup.round(, 2);
data.rectX = rectX;
data.ballX = ballX;

// Function to animate the ball
function animateBall() {
.during(function (pos) {
moveX = -pos * data.ballX;
window.moveX = moveX;
moveY = 0;
ball.attr({ transform: “translate(” + moveX + “,” + moveY + “)” });
.loop(true, false)
.after(function () {
$(“#start”).attr(“disabled”, true);

// Function to record position data
function recordPosition(event, angle = 13.5) {
if (event.keyCode == 32) {
data.ballPosition.push(distanceSetup.round( + moveX, 2));
const sum = data.ballPosition.reduce((a, b) => a + b, 0);
const ballPosLen = data.ballPosition.length;
data.avgBallPos = distanceSetup.round(sum / ballPosLen, 2);
const ball_sqr_distance = (data.squarePosition - data.avgBallPos) / data.px2mm;
const viewDistance = ball_sqr_distance / Math.radians(angle);
data.viewDistance_mm = distanceSetup.round(viewDistance, 2);
let counter = Number($(“#click”).text());
counter = counter - 1;
$(“#click”).text(Math.max(counter, 0));
if (counter <= 0) {
$(document).off(“keydown”, recordPosition);
// Add next step to advance in the experiment here
} else {

// Helper function to convert degrees to radians
Math.radians = function (degrees) {
return degrees * Math.PI / 180;

// Helper function to get card width
function getCardWidth() {
const cardWidth = 0; // Assign a default value (e.g., 0)

then running

python configureBlindSpot()

before the experiment and
python animateBall()
before the routine
while finally running
python recordPosition()
at the end of routine.

This has thus far only let to dispair. Can any one tell me if they have successfully implemented something similar in their experiments.

I really hope I can get some help here as I feel I am getting pretty deep into a rabbit hole

I’ve just updated my ScreenScale experiment to include a blind spot method of calculating visual angle and viewing distance. Comments welcome.

Blind Spot code | try it

This experiment updates my previous ScreenScale code to 2023.1.3 and adds a method of calculating visual angle (and viewing distance) using the blind spot method. The program uses the assumption that the centre of the blind spot is about 15 degrees from the fixation point.
Morys-Carter, W. L. (2023, July 6). Blind Spot [Computer software]. Pavlovia.