Skip to content

Stimulus Form Task

Try the Demo

The Stimulus Form task presents stimuli one-by-one (or in small groups) and collects custom ratings or responses through configurable form elements. Each stimulus is accompanied by a form containing sliders, text inputs, or multiple-choice questions that you design based on your research needs.

This flexible task allows systematic collection of multi-dimensional assessments for each stimulus, making it suitable for norming studies, validation research, and exploratory data collection.

Example Stimulus Form task

Example Stimulus Form showing a stimulus (purple sphere) with three types of form elements: a slider, radio buttons, and a text input field.

Quick Start

  1. Create or open an experiment from your Dashboard
  2. Click Add task and select "Stimulus Form"
  3. Select a stimulus set to rate
  4. Configure your form elements (sliders, text fields, or radio buttons) on the Parameters tab
  5. Preview your experiment to verify the layout

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

Alternative tasks

  • Use Drag & Rate for continuous two-dimensional ratings with spatial positioning
  • Use Label for simple categorization or tagging of stimuli
  • Use Gallery Select for selecting subsets of stimuli based on criteria
  • Use Dynamic Form for a single form not tied to stimulus presentation

Parameters

Customize the task by changing these on the Parameters tab of the task.

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.

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.

Stimulus Display

Item size

The size of the displayed stimulus as a percentage of screen width (%) or pixels (px). Default: 8. Valid range: 0.2 to 50.

Item unit

Unit for the item size. Options: % (percentage of screen width), px (pixels). Default: %.

Form Configuration

Configure the form elements that participants will use to provide responses about each stimulus.

Form Elements

A list of form elements to display. Each element has the following properties:

Name: The field name in your data (1-12 alphanumeric characters, no special characters)

Title: The label shown to participants (1-40 characters)

Kind: The type of input widget. Options:

  • Slider: Continuous scale from 0 to 1
  • Text: Free-text input field
  • Radio Buttons: Multiple-choice selection

Options: For Radio Buttons only, list the choices participants can select from (maximum 20 options, each up to 50 characters)

Stimuli per trial

How many stimuli to display simultaneously. Options:

  • 1: Single stimulus (most common)
  • 2: Pair of stimuli (for comparative judgments)
  • 3: Triplet (for triadic comparisons)

Default: 1.

Maximum number of trials

The task will present either all unique combinations of stimuli or this maximum number, whichever is smaller. Default: 2000. Valid range: 1 to 10,000.

Next button label

The text displayed on the button to advance to the next stimulus. Default: "Next".

Data

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

Trial-wise "annotations" (table rows), with one row per trial. Columns:

  • trial - numerical index of the trial
  • time_trial_start - timestamp when the stimulus and form were displayed (seconds since 1/1/1970)
  • time_trial_response - timestamp when the participant submitted the form (seconds since 1/1/1970)
  • stim1_id - meadows internal id of the first stimulus
  • stim1_name - filename of the first stimulus as uploaded
  • stim2_id - (if stimuli_per_trial ≥ 2) meadows internal id of the second stimulus
  • stim2_name - (if stimuli_per_trial ≥ 2) filename of the second stimulus
  • stim3_id - (if stimuli_per_trial = 3) meadows internal id of the third stimulus
  • stim3_name - (if stimuli_per_trial = 3) filename of the third stimulus
  • label - JSON string containing all form responses with field names as keys

The label column contains a JSON object with your custom form field names and their values. For example, if you configured fields named "pleasantness", "familiarity", and "category", the label might be:

{"pleasantness": "0.75", "familiarity": "0.32", "category": "animal"}

In the Tree structure, form responses are stored in the annotations array with the same structure as described above.

Analysis

Parse and Analyze Form Responses

The form responses are stored as JSON in the label column. Here's how to parse and analyze them:

In Google Sheets or Microsoft Excel, you can analyze form responses from your downloaded annotations data.

  1. Load Data: Import the Meadows_myExperiment_v1_annotations.csv file.
  2. Parse JSON: The label column contains JSON. You can:
  3. Use the "Split text to columns" feature with " and , as delimiters (works for simple cases)
  4. Or manually copy values for specific stimuli
  5. For more complex parsing, consider using Python or R
  6. Extract Field Values: If your JSON has {"pleasantness": "0.75"}, after splitting you can extract the numeric values
  7. Calculate Statistics:
  8. Mean: =AVERAGE(column)
  9. Standard deviation: =STDEV.S(column)
  10. Min/Max: =MIN(column) and =MAX(column)
  11. Create Pivot Table: Summarize ratings by stimulus using stim1_name as Row and average of your rating column as Value
  12. Visualize: Create histograms or bar charts to show rating distributions or per-stimulus averages
import pandas as pd
import json
import matplotlib.pyplot as plt

# Load the annotations data
df = pd.read_csv('Meadows_myExperiment_v1_annotations.csv')

# Parse the JSON label column
df_responses = pd.json_normalize(df['label'].apply(json.loads))

# Combine with stimulus information
df_full = pd.concat([df[['trial', 'stim1_name', 'time_trial_start', 
                          'time_trial_response']], df_responses], axis=1)

# Calculate response time in seconds
df_full['response_time'] = (df_full['time_trial_response'] - 
                             df_full['time_trial_start'])

# Example: Analyze a slider variable (assuming you have a 'pleasantness' field)
if 'pleasantness' in df_full.columns:
    df_full['pleasantness'] = pd.to_numeric(df_full['pleasantness'])

    print(f"Mean pleasantness: {df_full['pleasantness'].mean():.3f}")
    print(f"SD pleasantness: {df_full['pleasantness'].std():.3f}")

    # Plot distribution
    plt.figure(figsize=(10, 6))
    plt.hist(df_full['pleasantness'], bins=20, edgecolor='black')
    plt.xlabel('Pleasantness Rating')
    plt.ylabel('Frequency')
    plt.title('Distribution of Pleasantness Ratings')
    plt.show()

    # Top 10 most pleasant stimuli
    top_stimuli = (df_full.groupby('stim1_name')['pleasantness']
                   .mean()
                   .sort_values(ascending=False)
                   .head(10))
    print("\nTop 10 most pleasant stimuli:")
    print(top_stimuli)
library(tidyverse)
library(jsonlite)

# Load the annotations data
df <- read_csv('Meadows_myExperiment_v1_annotations.csv')

# Parse the JSON label column
df_responses <- df %>%
  mutate(parsed = map(label, fromJSON)) %>%
  unnest_wider(parsed)

# Calculate response time
df_responses <- df_responses %>%
  mutate(response_time = time_trial_response - time_trial_start)

# Example: Analyze a slider variable (assuming 'pleasantness')
if ('pleasantness' %in% colnames(df_responses)) {
  df_responses <- df_responses %>%
    mutate(pleasantness = as.numeric(pleasantness))

  # Summary statistics
  df_responses %>%
    summarise(
      mean_pleasantness = mean(pleasantness, na.rm = TRUE),
      sd_pleasantness = sd(pleasantness, na.rm = TRUE),
      n_ratings = n()
    ) %>%
    print()

  # Plot distribution
  ggplot(df_responses, aes(x = pleasantness)) +
    geom_histogram(bins = 20, fill = 'steelblue', color = 'black') +
    labs(x = 'Pleasantness Rating', y = 'Frequency',
         title = 'Distribution of Pleasantness Ratings') +
    theme_minimal()

  # Top 10 most pleasant stimuli
  df_responses %>%
    group_by(stim1_name) %>%
    summarise(mean_pleasantness = mean(pleasantness, na.rm = TRUE)) %>%
    arrange(desc(mean_pleasantness)) %>%
    head(10)
}

Analyze Categorical Responses

For radio button selections:

import pandas as pd
import json

# Load and parse data
df = pd.read_csv('Meadows_myExperiment_v1_annotations.csv')
df_responses = pd.json_normalize(df['label'].apply(json.loads))
df_full = pd.concat([df[['stim1_name']], df_responses], axis=1)

# Example: Analyze a categorical variable (assuming 'category' field)
if 'category' in df_full.columns:
    # Count frequency of each category
    category_counts = df_full['category'].value_counts()
    print(category_counts)

    # Crosstab: category by stimulus
    crosstab = pd.crosstab(df_full['stim1_name'], df_full['category'])
    print(crosstab)
library(tidyverse)
library(jsonlite)

# Load and parse data
df <- read_csv('Meadows_myExperiment_v1_annotations.csv')
df_responses <- df %>%
  mutate(parsed = map(label, fromJSON)) %>%
  unnest_wider(parsed)

# Example: Analyze categorical variable
if ('category' %in% colnames(df_responses)) {
  # Frequency table
  df_responses %>%
    count(category) %>%
    arrange(desc(n))

  # Crosstab by stimulus
  df_responses %>%
    count(stim1_name, category) %>%
    pivot_wider(names_from = category, values_from = n, values_fill = 0)
}