Skip to content

Card Measure Task

In this task, participants calibrate their screen by matching a resizable rectangle to a physical credit card or bank card. A gray rectangle appears on screen with adjustable handles, and participants resize it to match the dimensions of a standard bank card held against the screen. This determines the pixel density of the participant's display, allowing subsequent tasks to present stimuli in calibrated physical units (centimeters or millimeters).

Screen calibration using physical reference objects is a common method in online psychophysics research1. Since most credit cards follow the ISO/IEC 7810 ID-1 standard (85.60 × 53.98 mm), they provide a reliable, universally available calibration tool. This approach complements other calibration methods like the Virtual Chinrest for viewing distance, enabling accurate stimulus presentation in remote testing scenarios.

Quick Start

  1. Create or open an experiment from your Dashboard
  2. Click Add task and select "Card Measure"
  3. Instruct participants to have a credit/bank card ready
  4. Preview to ensure the task runs smoothly
  5. Use the measured pixel density to calibrate subsequent tasks

New to Meadows? See the Getting Started guide for a complete walkthrough.

Alternative tasks

Parameters

This task has minimal configuration options and uses primarily the general interface settings.

General Interface settings

Customize the instruction at the top of the page, as well as toolbar buttons. These apply to most task types on Meadows.

Instruction hint

Text that you can display during the task at the top of the page. We recommend instructing participants to adjust both width and height to match their card.

Extended instruction

A longer instruction that only appears if the participant hovers their mouse cursor over the hint.

Hint size

Whether to display the instruction, or hide it, and what font size to use.

Fullscreen button

Whether to display a button in the bottom toolbar that participants can use to switch fullscreen mode on and off.

How it works

The task follows this procedure:

  1. A gray rectangle with rounded corners appears in the center of the screen
  2. The rectangle displays blue corner handles and borders
  3. Participants hold a bank card or credit card flat against the screen
  4. Using the corner handles, participants resize the rectangle to match the card's dimensions
  5. The rectangle can be scaled but not moved or rotated
  6. When satisfied with the match, participants press the Enter key
  7. A confirmation dialog asks "Are you done matching the card shape?"
  8. Upon confirmation, the final pixel dimensions (width and height) are recorded

The pixel density is calculated by dividing the measured pixel dimensions by the known physical dimensions of a standard credit card (85.60 × 53.98 mm according to ISO/IEC 7810 ID-1).

Important Instructions for Participants

Participants should:

  • Adjust both width and height of the rectangle
  • Hold the card flat against the screen (not at an angle)
  • Match all four edges as precisely as possible
  • Use a standard credit or bank card (not loyalty cards or other non-standard cards)

Data

For general information about the various structures and file formats that you can download for your data see Downloads.

Trial-wise "events" (table rows), with typically one row per participant. Columns include:

  • trial - typically 0 for the single calibration measurement
  • time_trial_start - timestamp when the rectangle was displayed (seconds since 1/1/1970)
  • time_trial_response - timestamp when the participant confirmed (seconds since 1/1/1970)
  • label - encoded measurement in format {width}_{height} where both values are in pixels (e.g., 523.45_328.92)

Analysis

Calculate Pixel Density

Extract the measured dimensions and calculate pixels per millimeter or pixels per centimeter, which can be used to convert stimulus sizes between pixels and physical units.

import pandas as pd
import numpy as np

# Load the events data
df = pd.read_csv('Meadows_myExperiment_v1_events.csv')

# Standard credit card dimensions (ISO/IEC 7810 ID-1)
CARD_WIDTH_MM = 85.60
CARD_HEIGHT_MM = 53.98

# Filter for calibrate task events
calibrate_data = df[df['task_type'] == 'calibrate'].copy()

# Parse the label to extract measured dimensions
calibrate_data[['width_px', 'height_px']] = calibrate_data['label'].str.split('_', expand=True).astype(float)

# Calculate pixel density
calibrate_data['px_per_mm_width'] = calibrate_data['width_px'] / CARD_WIDTH_MM
calibrate_data['px_per_mm_height'] = calibrate_data['height_px'] / CARD_HEIGHT_MM
calibrate_data['px_per_mm_avg'] = (calibrate_data['px_per_mm_width'] + calibrate_data['px_per_mm_height']) / 2

# Convert to pixels per cm for convenience
calibrate_data['px_per_cm'] = calibrate_data['px_per_mm_avg'] * 10

# Display results
print("Screen Calibration Results:")
print(f"Measured dimensions: {calibrate_data['width_px'].iloc[0]:.1f} x {calibrate_data['height_px'].iloc[0]:.1f} pixels")
print(f"Pixel density: {calibrate_data['px_per_mm_avg'].iloc[0]:.3f} px/mm ({calibrate_data['px_per_cm'].iloc[0]:.2f} px/cm)")

# Calculate screen dimensions in cm (if you know the screen resolution)
screen_width_px = 1920  # example: replace with actual screen width
screen_height_px = 1080  # example: replace with actual screen height
screen_width_cm = screen_width_px / calibrate_data['px_per_cm'].iloc[0]
screen_height_cm = screen_height_px / calibrate_data['px_per_cm'].iloc[0]
print(f"Estimated screen size: {screen_width_cm:.1f} x {screen_height_cm:.1f} cm")
library(tidyverse)

# Load the events data
df <- read_csv('Meadows_myExperiment_v1_events.csv')

# Standard credit card dimensions (ISO/IEC 7810 ID-1)
CARD_WIDTH_MM <- 85.60
CARD_HEIGHT_MM <- 53.98

# Filter for calibrate task and parse dimensions
calibrate_data <- df %>%
  filter(task_type == 'calibrate') %>%
  separate(label, into = c('width_px', 'height_px'), sep = '_', convert = TRUE) %>%
  mutate(
    px_per_mm_width = width_px / CARD_WIDTH_MM,
    px_per_mm_height = height_px / CARD_HEIGHT_MM,
    px_per_mm_avg = (px_per_mm_width + px_per_mm_height) / 2,
    px_per_cm = px_per_mm_avg * 10
  )

# Display results
cat("Screen Calibration Results:\n")
cat(sprintf("Measured dimensions: %.1f x %.1f pixels\n", 
            calibrate_data$width_px[1], calibrate_data$height_px[1]))
cat(sprintf("Pixel density: %.3f px/mm (%.2f px/cm)\n", 
            calibrate_data$px_per_mm_avg[1], calibrate_data$px_per_cm[1]))

# Calculate screen dimensions in cm (if you know the screen resolution)
screen_width_px <- 1920  # example: replace with actual screen width
screen_height_px <- 1080  # example: replace with actual screen height
screen_width_cm <- screen_width_px / calibrate_data$px_per_cm[1]
screen_height_cm <- screen_height_px / calibrate_data$px_per_cm[1]
cat(sprintf("Estimated screen size: %.1f x %.1f cm\n", 
            screen_width_cm, screen_height_cm))

To extract pixel density in Excel or Google Sheets:

  1. Open the events.csv file
  2. Filter the task_type column for calibrate
  3. In a new column, extract width: =VALUE(LEFT(A2, FIND("_", A2) - 1)) (assuming label is in cell A2)
  4. In another column, extract height: =VALUE(RIGHT(A2, LEN(A2) - FIND("_", A2)))
  5. Calculate pixel density:
    • Width density: =B2/85.60 (where B2 is width in pixels)
    • Height density: =C2/53.98 (where C2 is height in pixels)
    • Average: =(D2+E2)/2
  6. Convert to px/cm by multiplying by 10

Convert Between Pixels and Physical Units

Once you have the pixel density, you can convert stimulus sizes between pixels and physical units for use in subsequent tasks.

# Using pixel density from previous analysis
px_per_mm = 3.78  # example value from calibration

def mm_to_pixels(mm, px_per_mm):
    """Convert millimeters to pixels"""
    return mm * px_per_mm

def pixels_to_mm(pixels, px_per_mm):
    """Convert pixels to millimeters"""
    return pixels / px_per_mm

def cm_to_pixels(cm, px_per_mm):
    """Convert centimeters to pixels"""
    return cm * 10 * px_per_mm

def pixels_to_cm(pixels, px_per_mm):
    """Convert pixels to centimeters"""
    return pixels / (px_per_mm * 10)

# Example: Create a 5cm stimulus
stimulus_size_cm = 5.0
stimulus_size_px = cm_to_pixels(stimulus_size_cm, px_per_mm)
print(f"A {stimulus_size_cm}cm stimulus should be {stimulus_size_px:.1f} pixels")

# Example: Measure a stimulus in pixels and convert to cm
measured_px = 189
measured_cm = pixels_to_cm(measured_px, px_per_mm)
print(f"A {measured_px}px stimulus is {measured_cm:.2f}cm")
% Using pixel density from previous analysis
px_per_mm = 3.78;  % example value from calibration

% Convert millimeters to pixels
function pixels = mm_to_pixels(mm, px_per_mm)
    pixels = mm * px_per_mm;
end

% Convert pixels to millimeters
function mm = pixels_to_mm(pixels, px_per_mm)
    mm = pixels / px_per_mm;
end

% Convert centimeters to pixels
function pixels = cm_to_pixels(cm, px_per_mm)
    pixels = cm * 10 * px_per_mm;
end

% Convert pixels to centimeters
function cm = pixels_to_cm(pixels, px_per_mm)
    cm = pixels / (px_per_mm * 10);
end

% Example: Create a 5cm stimulus
stimulus_size_cm = 5.0;
stimulus_size_px = cm_to_pixels(stimulus_size_cm, px_per_mm);
fprintf('A %.1fcm stimulus should be %.1f pixels\n', ...
        stimulus_size_cm, stimulus_size_px);

% Example: Measure a stimulus in pixels and convert to cm
measured_px = 189;
measured_cm = pixels_to_cm(measured_px, px_per_mm);
fprintf('A %dpx stimulus is %.2fcm\n', measured_px, measured_cm);

Combine with Virtual Chinrest for Visual Angle

For complete calibration, combine card measure (for pixel density) with virtual chinrest (for viewing distance) to calculate visual angles.

import numpy as np

# From card measure
px_per_mm = 3.78

# From virtual chinrest
viewing_distance_mm = 600

def visual_angle_to_pixels(angle_deg, distance_mm, px_per_mm):
    """Convert visual angle in degrees to pixels"""
    size_mm = 2 * distance_mm * np.tan(np.radians(angle_deg / 2))
    return size_mm * px_per_mm

def pixels_to_visual_angle(pixels, distance_mm, px_per_mm):
    """Convert pixels to visual angle in degrees"""
    size_mm = pixels / px_per_mm
    angle_rad = 2 * np.arctan(size_mm / (2 * distance_mm))
    return np.degrees(angle_rad)

# Example: Create a stimulus that is 3 degrees of visual angle
desired_angle = 3.0
stimulus_px = visual_angle_to_pixels(desired_angle, viewing_distance_mm, px_per_mm)
print(f"For {desired_angle}° visual angle: {stimulus_px:.1f} pixels")

Best Practices

  1. Task Placement: Place the Card Measure task early in your experiment, before tasks that require calibrated dimensions
  2. Clear Instructions: Ensure participants understand they should adjust both width and height
  3. Card Type: Instruct participants to use a standard credit or bank card (not loyalty cards or gift cards which may have non-standard dimensions)
  4. Card Orientation: If orientation matters for your study, specify whether the card should be horizontal (landscape) or vertical (portrait)
  5. Accuracy Check: Consider having participants repeat the measurement if the derived pixel density seems unrealistic (e.g., less than 2 or greater than 6 px/mm for typical displays)
  6. Combine Methods: For highest accuracy in visual angle calculations, use both Card Measure and Virtual Chinrest

Troubleshooting

Unrealistic pixel density values

Check if participants are using non-standard cards. Also verify they're holding the card flat against the screen, not at an angle.

Participants can't see the handles

The corner handles are blue and may be difficult to see on some displays. Consider adding instructions to click on the rectangle first to ensure it's selected.

Rectangle won't resize

Participants should drag from the corner handles (blue squares), not from the edges or center of the rectangle.

References


  1. Woods, A. T., Velasco, C., Levitan, C. A., Wan, X., & Spence, C. (2015). Conducting perception research over the internet: a tutorial review. PeerJ, 3, e1058. doi:10.7717/peerj.1058