Open Scale Definition (OSD) — Format Specification

Complete technical reference for the JSON-based format used to encode psychological and behavioral measurement scales. This document is sufficient to implement a compliant scale from scratch.

1. Overview

OSD (Open Scale Definition) is a JSON-based format for encoding psychological and behavioral measurement scales in a structured, machine-readable form. A scale is defined by a definition file ({CODE}.json) that captures structure, items, dimensions, and scoring rules, and one or more translation files ({CODE}.{lang}.json) that supply all display strings in a given language. For distribution and external hosting, these are combined into a single OSD bundle ({CODE}.osd).

The format is designed to be platform-neutral: the same definition files are read by PEBL (the desktop runner), the web-based scale runner, conversion tools that export to REDCap / LimeSurvey / Qualtrics, and any custom implementation that follows this specification.

Key design principles:

  • Separation of structure from language: the .json definition never contains display strings directly; all text passes through translation keys.
  • Flat translation files: the .{lang}.json file is a simple key/value object, easy to edit in any text editor or import into a translation management system.
  • Explicit scoring: reverse-coding and dimension membership are declared in the definition, not inferred from item order.
  • Forward-compatible: unknown keys are silently ignored, so new runners can add fields without breaking existing definitions.
  • Single-file distribution: the .osd bundle combines the definition and all translations into one file for easy hosting — point the web runner directly at it with one URL.

2. File Naming & Directory Structure

Each scale lives in its own subdirectory under scales/. The directory name must match the scale code exactly.

scales/
  {CODE}/
    {CODE}.osd           <-- scale definition + translations (single JSON file)
    screenshot.png       <-- (optional) screenshot shown in the catalogue
    README.md            <-- (optional) description and usage notes
    LICENSE.txt          <-- (optional) license evidence

CODE Conventions

  • Uppercase letters, digits, and hyphens only. No spaces.
  • Use the established abbreviation for well-known scales: PHQ9, GAD7, SF36, BDI2.
  • If no abbreviation exists, invent a short descriptive code and document it in scale_info.abbreviation.
  • Examples of valid codes: PHQ9, MOSSLP, IPIP-Big5-50, EAS3.

Language Codes

Translation keys within the translations object use ISO 639-1 two-letter language codes: en, de, fr, es, nl, etc. An English translation ("en") is required. Additional languages are optional and are embedded in the same .osd file.

The .osd Format

A .osd file is a plain JSON file that packages the scale definition and all translations into a single document. It is the standard format used throughout OpenScales: in the repository, the web runner, the PEBL desktop launcher, and PEBLHub. A researcher hosting a scale on their own website needs only one file, and the web runner can be pointed at it with a single URL.

Bundle structure

{
  "osd_version": "1.0",
  "definition": {
    /* complete contents of {CODE}.json */
  },
  "translations": {
    "en": { /* complete contents of {CODE}.en.json */ },
    "de": { /* complete contents of {CODE}.de.json */ },
    "fr": { /* ... */ }
  }
}
KeyTypeDescription
osd_versionstring Format version. Currently "1.0". Parsers should warn on unknown versions but still attempt to read the file.
definitionobject The full scale definition — identical in content to {CODE}.json.
translationsobject Map of language code → flat string dictionary. Each value is identical in content to the corresponding {CODE}.{lang}.json file. May be empty {} if no translations are available yet.

Using a .osd file with the web runner

Because a .osd file is plain JSON, it can be fetched from any URL that serves it with CORS headers — including OSF, GitHub raw, GitLab raw, and ordinary web hosting. No special server configuration is required.

Direct URL — share a single link that runs the scale straight from your hosted file:

<!-- OSF -->
scale-runner.html?osd=https://osf.io/abc123/download&lang=en

<!-- GitHub raw -->
scale-runner.html?osd=https://raw.githubusercontent.com/user/repo/main/GAD7.osd

<!-- GitLab raw -->
scale-runner.html?osd=https://gitlab.com/user/repo/-/raw/main/GAD7.osd

<!-- Personal/lab website -->
scale-runner.html?osd=https://lab.example.org/scales/GAD7.osd&lang=fr

JavaScript API:

ScaleRunner.mount(container, {
  osdURL:      'https://osf.io/abc123/download',
  language:    'de',        // falls back to 'en' if 'de' not present in bundle
  participant: participantId,
  onComplete:  handleResult,
});

Using a .osd file with PEBL

The PEBL launcher reads .osd files directly and unpacks them to create a valid PEBL experiment. Once unpacked, you can use the standard PEBL schemes for translations (language files) and parameter-setting (parameter files). Place the .osd file in your PEBL scales/ directory and select it from the launcher's ScaleBuilder interface.

The .osd file is also available for download directly from each scale's page on this website. The MIME type for serving .osd files is application/json; no special server configuration is required.

3. Top-Level Keys of the Definition Object

These keys appear inside the "definition" object of the .osd file.

Key Type Required Description
scale_info object Yes Metadata about the scale: name, code, citation, license, etc.
likert_options object or null Yes Default response scale shared by all likert-type items. Set to null if no items use the likert type.
parameters object No Named runtime parameters that can be overridden via URL or launcher config.
dimensions array Yes Scored subscales. Each element defines one dimension; the array may be empty for unscored instruments.
items array Yes All administered items in presentation order, including instruction screens.
scoring object Yes Scoring rules keyed by dimension id. May be an empty object if there are no scored dimensions.
report object No HTML report configuration: template, sections to include, header, footer citations.
data_output object No Output file naming templates and column lists for CSV output.
default_required boolean No Overrides the type-based required defaults for all items in the scale. Per-item required fields take precedence over this setting. See Section 8a.

4. scale_info Object

Metadata that identifies the scale and satisfies attribution and licensing requirements.

Key Type Required Description
name string Yes Full human-readable name of the scale, e.g. "Patient Health Questionnaire-9".
code string Yes Machine code; must match the directory name and filename prefix exactly, e.g. "PHQ9".
abbreviation string No Common short name displayed in badges and compact views, e.g. "PHQ-9".
description string Yes 1–3 sentence plain-text description of the scale, what it measures, and the score range.
citation string Yes Full APA-style citation for the original publication, including DOI where available.
license string Yes SPDX identifier or plain-text license name, e.g. "Public Domain", "CC BY 4.0", "CC BY-NC 4.0".
version string No Version of this OSD definition file (not the scale itself). Defaults to "1.0".
url string No DOI or canonical URL for the scale, e.g. "https://doi.org/10.1046/j.1525-1497.2001.016009606.x".
domain string No Domain hint for the manifest and catalogue UI (e.g. "depression", "physical activity"). Overrides keyword-based auto-detection.
debrief_key string No Translation key for the message shown to the participant on completion. Defaults to "debrief".

Example

"scale_info": {
  "name":         "Patient Health Questionnaire-9",
  "code":         "PHQ9",
  "abbreviation": "PHQ-9",
  "description":  "9-item depression screening tool measuring symptom severity over the past 2 weeks. Scores range from 0 to 27.",
  "citation":     "Kroenke, K., Spitzer, R. L., & Williams, J. B. (2001). The PHQ-9: Validity of a brief depression severity measure. Journal of General Internal Medicine, 16(9), 606-613. https://doi.org/10.1046/j.1525-1497.2001.016009606.x",
  "license":      "Public Domain",
  "version":      "1.0",
  "url":          "https://doi.org/10.1046/j.1525-1497.2001.016009606.x"
}

5. likert_options Object

Defines the single shared response scale applied to every item whose type is "likert". If the scale has no likert-type items (all items are multi, short, number, etc.), set likert_options to null.

Key Type Required Description
points integer Yes Number of response options, e.g. 4 for a 4-point scale.
min integer Yes Numeric value of the first (leftmost) option. Typically 0 or 1.
max integer Yes Numeric value of the last (rightmost) option. Must equal min + points - 1.
labels array of strings Yes Translation keys for the option labels, one per point. Length must equal points. Each key must be defined in the translation file.
question_head string No Translation key for a shared question stem shown above all likert items, e.g. "question_head" resolving to "Over the last 2 weeks, how often…".

Example — PHQ-9 (4-point, 0-based)

"likert_options": {
  "points":        4,
  "min":           0,
  "max":           3,
  "labels":        ["likert_1", "likert_2", "likert_3", "likert_4"],
  "question_head": "question_head"
}
Note: When likert_options is null, every scored item must be of a type other than likert (typically multi or number), and option values must be set explicitly in each item's options array. See the IPAQ definition for a real example.

6. parameters Object

An optional object that declares named runtime parameters. Parameters can be overridden at launch time via URL query strings (?param_NAME=value) or via the launcher configuration dialog, without editing the definition file. Each key is the parameter name; the value is a parameter descriptor object.

Field Type Required Description
type string Yes One of "boolean", "integer", "number", "string", "choice". Used for validation and UI widget selection.
default any Yes Value used when the parameter is not overridden at runtime.
description string No Human-readable description for documentation and the launcher UI.
options array For "choice" type Array of allowed string values. Only used when type is "choice".

Reserved Parameter Names

Runners give these parameter names special treatment in addition to normal parameter substitution. Scales that require specific behavior should declare these in their parameters block with the appropriate default.

Name Type Default Effect
shuffle_questions boolean false Randomize item order within randomization groups (see random_group in Section 8a). When declared in the OSD, the declared default overrides the runner's built-in default.
show_header boolean true Whether to display the scale title above the questionnaire. Set to false for scales where revealing the title could bias responses (e.g., burnout or susceptibility scales administered in a battery).

Example

"parameters": {
  "shuffle_questions": {
    "type":        "boolean",
    "default":     false,
    "description": "Randomize item presentation order"
  },
  "show_header": {
    "type":        "boolean",
    "default":     false,
    "description": "Hide scale title to avoid response bias"
  },
  "system_name": {
    "type":        "string",
    "default":     "the system",
    "description": "Name of the system being evaluated (inserted into item text)"
  },
  "scale_version": {
    "type":        "choice",
    "default":     "full",
    "options":     ["full", "short", "screening"],
    "description": "Which version of the scale to administer"
  }
}

Parameters are overridden at runtime via URL query strings (?param_shuffle_questions=1) or via launcher configuration. Parameters are referenced by name in the enabled_param field of a dimension (see Section 7). Parameter values can also be substituted into item text using {param_name} syntax in translation strings.

7. dimensions Array

Each element describes one scored subscale. The array determines what subscale scores are computed and reported. Items may belong to zero, one, or more dimensions; dimension membership is declared in the scoring object, not here.

Field Type Required Description
id string Yes Machine key used as the key in the scoring object and in CSV column names. Must be unique within the scale.
name string Yes Human-readable name displayed in the report and catalogue.
abbreviation string No Short label for compact display, e.g. "PF" for Physical Functioning.
description string No Plain-text description including score range and direction, e.g. "Range 0–27; higher = greater severity."
enabled_param string or null No If set to a parameter name, this dimension is only scored when that parameter is truthy at runtime. Set to null (or omit) to always score.

Example — SF-36 multi-dimension excerpt

"dimensions": [
  {
    "id":           "pf",
    "name":         "Physical Functioning",
    "abbreviation": "PF",
    "description":  "Extent to which health limits physical activities. Range: 10–30 (higher = less limitation).",
    "enabled_param": null
  },
  {
    "id":           "ewb",
    "name":         "Emotional Well-Being",
    "abbreviation": "EWB",
    "description":  "Extent of anxiety and depression. Range: 5–30 (higher = better mental health).",
    "enabled_param": null
  }
]

8. items Array

The ordered list of every item presented to the participant, including instruction screens, image displays, and scored questions. Items are shown in array order unless branching conditions redirect flow.

8a. Common Fields (All Item Types)

Field Type Required Description
id string Yes Unique identifier for this item within the scale. Used as the key in scoring.item_coding and as the CSV column name for the response.
text_key string Yes Translation key whose value is the question or instruction text displayed to the participant. Must be defined in the translation file.
type string Yes Item type. See the type reference table (Section 8b) for valid values.
dimension string or null No Dimension id this item contributes to, for informational/display purposes. Scoring membership is authoritative in the scoring object.
visible_when object or null No Skip logic condition. Item is shown only when the condition evaluates to true. See Section 8d for syntax.
random_group integer varies Randomization group for shuffle behavior when shuffle_questions is active. 0 = fixed position; 1+ = shuffle within that numbered group (scoped to the item's section). Defaults: inst items and items with visible_when default to 0 (fixed); all others default to 1 (shuffled).
required boolean varies by type Whether the item must be answered before advancing. Overrides type-based defaults and the scale-level default_required field. Scored types (likert, vas, multi, etc.) default to required; text-entry types (short, long) default to optional.

8b. Item Type Reference

Type Description Key Extra Fields
inst Instruction or transition screen. Text is displayed but no response is recorded. Not included in scoring. Images can be embedded via <img> in the translation string. (none beyond common fields)
section Section boundary marker. Begins a new logical grouping of items. All items following the marker (up to the next section marker) belong to this section. See Section 8e. title_key (optional), revisable (boolean, default true), randomize (object, optional), visible_when (skips entire section)
likert Single-select using the global likert_options response scale. The most common type for traditional questionnaire items. (none beyond common fields)
multi Single-select with a custom set of labeled options. Use this when items need different response options from the global likert scale. options: array of {"text_key": "…", "value": N}
multicheck Multi-select (checkbox) with custom options. The recorded value is a comma-separated list of selected values. options: array of {"text_key": "…", "value": N}
short Single-line free-text entry. Supports optional validation constraints (see Section 8f). maxlength (optional); validation object (optional)
long Multi-line free-text entry (textarea). Supports optional validation. maxlength, rows, cols (all optional)
number Numeric entry with optional range and step constraints. min, max, step (all optional)
date Date picker entry. Recorded as an ISO 8601 date string. (none)
vas Visual analog scale — a continuous horizontal slider. min, max, min_label, max_label, step
grid Matrix question: a set of sub-rows each rated on the global likert_options scale. rows: array of row translation keys; columns: array of column translation keys
rank Rank-ordering task. Participant drags options into a preferred order. options: array of {"text_key": "…", "value": N}
dropdown Single-select from a dropdown list. Useful for long option lists. options: array of {"text_key": "…", "value": N}
constant_sum Participant allocates a fixed total of points across several options. options: array of {"text_key": "…", "value": N}; total: integer total to distribute
semantic_differential Bipolar adjective pair scale; participant rates on a continuum between two poles. min_label, max_label, min, max

8c. options for multi / multicheck / dropdown / rank

Each element of the options array is an object with two required fields:

Field Type Description
text_key string Translation key for the option label. Must be defined in the translation file.
value integer or float Numeric value recorded when this option is selected. Used directly in scoring.
Reverse coding for multi items: The item_coding: -1 flag in the scoring object does not apply to multi (or multicheck / dropdown) items — only to likert items. To reverse-code a multi item, pre-reverse the value fields in the options array. For example, if "Strongly Agree" would normally score 1 on a forward item, set "value": 5 for the reverse item and use item_coding: 1 in the scoring block. See the SF-36 definition (items sf36_q11b, sf36_q11d) for real examples.

8d. visible_when (Skip Logic)

An optional condition object that controls whether this item is shown. The item appears only when the condition evaluates to true; it is skipped (and its response recorded as "NA") when the condition is false. A missing or null visible_when means always show.

Simple condition (previous item's response)

{
  "id": "followup1",
  "text_key": "followup1",
  "type": "likert",
  "visible_when": {
    "item": "screen1",
    "operator": "equals",
    "value": 1
  }
}

Condition on a parameter value

{
  "visible_when": {
    "parameter": "show_followup",
    "operator": "equals",
    "value": true
  }
}

Compound conditions

Use "all" (logical AND) or "any" (logical OR) to combine conditions:

{
  "visible_when": {
    "all": [
      { "item": "q1", "operator": "equals", "value": "Yes" },
      { "parameter": "show_followup", "operator": "equals", "value": true }
    ]
  }
}

Operators

Operator Description
"equals"Exact match
"not_equals"Not equal
"greater_than"Numeric greater than
"less_than"Numeric less than
"in"Value is in array (provide "value": [...])
"not_in"Value is not in array
"is_answered"Item has been answered (no value needed)
"is_not_answered"Item has not been answered

8e. Sections

A section item creates a logical boundary within the items list. All items after a section marker (up to the next marker) belong to that section. Sections control randomization scope, skip logic, and optional back-navigation behavior.

{
  "items": [
    { "id": "inst1", "type": "inst", "text_key": "intro_text" },
    {
      "id": "sec_demographics",
      "type": "section",
      "title_key": "demographics_title"
    },
    { "id": "q1", "type": "short", "text_key": "q1" },
    { "id": "q2", "type": "multi",  "text_key": "q2", "options": [...] },
    {
      "id": "sec_main",
      "type": "section",
      "title_key": "main_title",
      "randomize": { "method": "shuffle", "fixed": ["q3"] }
    },
    { "id": "q3", "type": "likert", "text_key": "q3" },
    { "id": "q4", "type": "likert", "text_key": "q4" }
  ]
}
Field Required Description
id Yes Unique identifier for this section.
type Yes Must be "section".
title_key No Translation key for an optional section heading displayed to the participant.
revisable No Default true. When false, responses in this section are final — runners must not show a Back button for items in this section. Omit when true (the default).
visible_when No Skips the entire section (and all its items) when the condition is false. Evaluated once when the section marker is reached.
randomize No Randomize item order within this section. Currently supports {"method": "shuffle", "fixed": ["item_id", ...]}. The fixed list pins items in their defined positions (equivalent to setting random_group: 0).

Implicit first section: Items appearing before the first section marker are in an implicit unnamed section (always shown, revisable by default). To control its properties, place a section marker as the very first item.

8f. Input Validation

For short and long items, a validation object specifies constraints checked before advancing. Multiple constraints may coexist; each has a paired *_error key naming the translation string for the error message shown when that constraint fails.

{
  "id": "age",
  "type": "short",
  "text_key": "age_question",
  "validation": {
    "number_min": 0,
    "number_min_error": "age_too_low",
    "number_max": 150,
    "number_max_error": "age_too_high"
  }
}

{
  "id": "bio",
  "type": "long",
  "text_key": "bio_question",
  "validation": {
    "min_words": 10,
    "min_words_error": "bio_too_short",
    "max_length": 500,
    "max_length_error": "bio_too_long"
  }
}

{
  "id": "hobbies",
  "type": "multicheck",
  "text_key": "hobbies_question",
  "options": [...],
  "validation": {
    "min_selected": 1,
    "min_selected_error": "hobbies_min",
    "max_selected": 3,
    "max_selected_error": "hobbies_max"
  }
}
Constraint Applies to Description
min_lengthshort, longMinimum character count
max_lengthshort, longMaximum character count
min_wordsshort, longMinimum word count
max_wordsshort, longMaximum word count
number_minshortMinimum numeric value (also restricts input to digits)
number_maxshortMaximum numeric value
patternshortRegular expression — response must match
min_selectedmulticheckMinimum options checked
max_selectedmulticheckMaximum options checked

Item Examples

// A standard likert item
{ "id": "phq1", "text_key": "phq1", "type": "likert" }

// A multi item with custom options (pre-reversed for scoring)
{
  "id": "sf36_q11b",
  "text_key": "sf36_q11b",
  "type": "multi",
  "options": [
    { "text_key": "sf36_hp1", "value": 5 },
    { "text_key": "sf36_hp2", "value": 4 },
    { "text_key": "sf36_hp3", "value": 3 },
    { "text_key": "sf36_hp4", "value": 2 },
    { "text_key": "sf36_hp5", "value": 1 }
  ]
}

// An instruction screen (not scored)
{ "id": "sf36_inst1", "text_key": "sf36_inst1", "type": "inst" }

// A number item with constraints
{
  "id": "age",
  "text_key": "age_question",
  "type": "number",
  "min": 0,
  "max": 120,
  "step": 1
}

// An item shown only when a prior item equals 1
{
  "id": "followup1",
  "text_key": "followup1",
  "type": "likert",
  "visible_when": { "item": "screen1", "operator": "equals", "value": 1 }
}

// A section marker with shuffle (keeps q3 in place)
{
  "id": "sec_main",
  "type": "section",
  "title_key": "main_title",
  "randomize": { "method": "shuffle", "fixed": ["q3"] }
}

9. scoring Object

Keyed by dimension id. Each value is a scoring definition object specifying which items contribute to the dimension score and how they are aggregated.

Field Type Required Description
method string Yes One of: "sum_coded", "mean_coded", "weighted_sum", "sum_correct", "max", "min". See the method table below.
items array of strings See note Item id values to include. Only items listed in item_coding with a non-zero value are included in the score. Required unless scores is used instead.
scores array of strings No Dimension IDs whose already-computed scores are used as inputs, instead of or alongside items. Enables higher-order scores (e.g., summing subscale scores). item_coding applies to score references just as to item references. Runners must evaluate blocks in dependency order.
item_coding object Yes Maps each item/score id to 1 (forward), -1 (reverse), or 0 (exclude). For likert items, -1 reverses using min + max − response. For multi items, -1 has no effect — pre-reverse the option values instead. Items absent from item_coding are excluded.
weights object For weighted_sum Per-item weight values applied to each item's contribution.
correct_answers object For sum_correct Maps each item id to an array of acceptable answers (case-insensitive matching recommended). E.g. {"q1": ["5", "five"]}.
description string No Human-readable description of the score range, direction, and interpretation. Shown in the catalogue and report.
norms object or null No Interpretation thresholds. See Section 9a. Set to null to omit.

Scoring Methods

Method Description
sum_codedSum of coded item/score contributions
mean_codedMean of coded item/score contributions
weighted_sumWeighted sum; requires a weights object
sum_correctCount of correct answers; requires a correct_answers object
maxMaximum value across inputs (e.g., worst symptom across alternative items)
minMinimum value across inputs

Scoring Calculation

For a likert item with item_coding: 1: contribution = response value.

For a likert item with item_coding: -1: contribution = likert_options.min + likert_options.max − response.

For sum_coded: dimension score = sum of all item contributions.
For mean_coded: dimension score = mean of all item contributions.
For max / min: dimension score = maximum/minimum response across all forward-coded inputs (useful for scoring symptom domains as the worst of several alternative items, e.g. the QIDS-SR sleep subscale).

Using scores for higher-order aggregation

Some scales compute a total score by summing subscale scores rather than raw items. Use the scores field to reference already-computed dimension scores:

"scoring": {
  "sleep": {
    "method": "max",
    "items": ["item_falling_asleep", "item_sleep_night", "item_waking_up", "item_sleeping_too_much"],
    "item_coding": {
      "item_falling_asleep": 1, "item_sleep_night": 1,
      "item_waking_up": 1, "item_sleeping_too_much": 1
    }
  },
  "QIDS_total": {
    "method": "sum_coded",
    "scores": ["sleep", "sad_mood", "appetite_weight", "concentration",
               "self_view", "death_suicide", "interest", "energy", "psychomotor"],
    "item_coding": {
      "sleep": 1, "sad_mood": 1, "appetite_weight": 1, "concentration": 1,
      "self_view": 1, "death_suicide": 1, "interest": 1, "energy": 1, "psychomotor": 1
    },
    "description": "Sum of 9 symptom domain scores (0–27)"
  }
}

9a. norms Object

An optional object containing an array of threshold objects used by the runner and report template to display interpretation labels (e.g. "Minimal", "Mild", "Moderate") alongside the score.

"norms": {
  "thresholds": [
    { "min": 0,  "max": 4,  "label": "Minimal" },
    { "min": 5,  "max": 9,  "label": "Mild" },
    { "min": 10, "max": 14, "label": "Moderate" },
    { "min": 15, "max": 19, "label": "Moderately Severe" },
    { "min": 20, "max": 27, "label": "Severe" }
  ]
}

Each threshold's min and max are inclusive. Thresholds should be listed in ascending order and must cover the full possible score range without gaps or overlaps. The label is plain text (not a translation key) so it appears directly in reports.

Scoring Example — PHQ-9

"scoring": {
  "depression": {
    "method": "sum_coded",
    "items": ["phq1","phq2","phq3","phq4","phq5","phq6","phq7","phq8","phq9"],
    "item_coding": {
      "phq1": 1, "phq2": 1, "phq3": 1, "phq4": 1, "phq5": 1,
      "phq6": 1, "phq7": 1, "phq8": 1, "phq9": 1
    },
    "description": "Sum of all items (0–27). Severity: 0-4 minimal, 5-9 mild, 10-14 moderate, 15-19 moderately severe, 20-27 severe.",
    "norms": {
      "thresholds": [
        { "min": 0,  "max": 4,  "label": "Minimal" },
        { "min": 5,  "max": 9,  "label": "Mild" },
        { "min": 10, "max": 14, "label": "Moderate" },
        { "min": 15, "max": 19, "label": "Moderately Severe" },
        { "min": 20, "max": 27, "label": "Severe" }
      ]
    }
  }
}

Scoring Example — SF-36 (mixed forward/reverse coding)

"ef": {
  "method": "sum_coded",
  "items": ["sf36_q9a", "sf36_q9e", "sf36_q9g", "sf36_q9i"],
  "description": "Energy/Fatigue: sum of 4 items (range 4–24; higher = more energy).",
  "item_coding": {
    "sf36_q9a": -1,
    "sf36_q9e": -1,
    "sf36_q9g":  1,
    "sf36_q9i":  1
  }
}

10. report Object

Configures the HTML report generated after scale completion and available for download from the data dashboard.

Field Type Description
template string Report template name. Currently only "standard" is supported.
include array of strings Sections to include in the report. Valid values: "timestamp", "completion_time", "dimension_scores". Add or remove values to control report content.
header string Plain text (or a translation key) shown at the top of the report, typically describing what the report shows and any caveats about score interpretation.
footer_refs array of strings Citation strings shown at the bottom of the report. HTML is allowed (e.g. links to DOIs).

Example

"report": {
  "template":    "standard",
  "include":     ["timestamp", "completion_time", "dimension_scores"],
  "header":      "PHQ-9 Depression Severity Report. Scores range from 0 to 27.",
  "footer_refs": [
    "Kroenke, K., Spitzer, R. L., & Williams, J. B. (2001). The PHQ-9. Journal of General Internal Medicine, 16(9), 606-613."
  ]
}

11. data_output Object

Controls how output data files are named and what columns they contain. All fields are optional; omitting data_output uses runner defaults.

Field Type Description
individual_file string Filename template for per-participant CSV output. {subnum} is replaced with the participant identifier at runtime. Example: "PHQ9-{subnum}.csv".
pooled_file string Filename for the pooled (all-participants) CSV that is appended to on each run. Example: "PHQ9-pooled.csv".
report_file string Filename template for the HTML report. Example: "PHQ9-report-{subnum}.html".
columns string Comma-separated list of column names for the individual-participant CSV. Common columns: subnum, order, timestamp, question_id, text_key, dimension, coding, response, rt.
pooled_columns string Comma-separated list of column names for the pooled CSV. Often a subset of columns. Set to an empty string to use runner defaults.

Example

"data_output": {
  "individual_file": "PHQ9-{subnum}.csv",
  "pooled_file":     "PHQ9-pooled.csv",
  "report_file":     "PHQ9-report-{subnum}.html",
  "columns":         "subnum,order,timestamp,question_id,text_key,dimension,coding,response,rt",
  "pooled_columns":  ""
}

12. Translations (translations Object)

The translations object sits at the root level of the .osd file (alongside osd_version and definition). It is a flat JSON object per language, mapping string keys to translated display strings. Every key referenced in the definition must have an entry for the chosen language.

Rules

  • Each language is keyed by its ISO 639-1 code: "en", "de", etc.
  • All values are strings. HTML tags are allowed: <b>, <em>, <i>, <br>, <a href="…">.
  • Every text_key in items, every key in likert_options.labels, likert_options.question_head, and every text_key in options arrays must be present.
  • The key "debrief" (or the key named in scale_info.debrief_key) must be present. Its value is the message shown to the participant on completion.
  • English ("en") is required. Additional languages are optional but encouraged.
  • Keys not referenced by the definition are ignored.

Example — PHQ-9 English translations

{
  "likert_1":     "not at all",
  "likert_2":     "several days",
  "likert_3":     "more than half the days",
  "likert_4":     "nearly every day",
  "question_head": "Over the <b>last 2 weeks</b>, how often have you been bothered by any of the following problems?",
  "phq1":  "Little interest or pleasure in doing things",
  "phq2":  "Feeling down, depressed, or hopeless",
  "phq3":  "Trouble falling or staying asleep, or sleeping too much",
  "phq4":  "Feeling tired or having little energy",
  "phq5":  "Poor appetite or overeating",
  "phq6":  "Feeling bad about yourself — or that you are a failure or have let yourself or your family down",
  "phq7":  "Trouble concentrating on things, such as reading the newspaper or watching television",
  "phq8":  "Moving or speaking so slowly that other people could have noticed? Or the opposite — being so fidgety or restless that you have been moving around a lot more than usual",
  "phq9":  "Thoughts that you would be better off dead or of hurting yourself in some way",
  "debrief": "Thank you for completing this questionnaire."
}

13. Complete Minimal Example

A fully working OSD definition for a fictional 3-item scale: the Example Anxiety Screen (EAS3). This demonstrates the minimum viable structure: 3 likert items, 1 dimension, sum-coded scoring, and norms.

scales/EAS3/EAS3.osd

{
  "osd_version": "1.0",
  "definition": {
    "scale_info": {
      "name":        "Example Anxiety Screen",
      "code":        "EAS3",
      "abbreviation": "EAS-3",
      "description": "A fictional 3-item anxiety screening scale for demonstration purposes.",
      "citation":    "Author, A. B. (2024). The EAS-3: A demonstration scale. Example Journal, 1(1), 1-5.",
      "license":     "CC0",
      "version":     "1.0"
    },
    "implementation": {
      "author": "Shane T. Mueller",
      "organization": "OpenScales Project",
      "date": "2026-04-01",
      "license": "CC BY 4.0",
      "license_url": "https://creativecommons.org/licenses/by/4.0/"
    },
    "likert_options": {
      "points": 3,
      "min":    0,
      "max":    2,
      "labels": ["eas_never", "eas_sometimes", "eas_often"],
      "question_head": "eas_head"
    },
    "dimensions": [
      {
        "id":          "anxiety",
        "name":        "Anxiety Severity",
        "description": "Sum of all 3 items. Range 0–6; higher = greater anxiety."
      }
    ],
    "items": [
      { "id": "eas_inst", "text_key": "eas_inst", "type": "inst" },
      { "id": "eas1", "text_key": "eas1", "type": "likert" },
      { "id": "eas2", "text_key": "eas2", "type": "likert" },
      { "id": "eas3", "text_key": "eas3", "type": "likert" }
    ],
    "scoring": {
      "anxiety": {
        "method": "sum_coded",
        "items":  ["eas1", "eas2", "eas3"],
        "item_coding": { "eas1": 1, "eas2": 1, "eas3": 1 },
        "description": "Sum of all items (range 0–6).",
        "norms": {
          "thresholds": [
            { "min": 0, "max": 2, "label": "Minimal" },
            { "min": 3, "max": 4, "label": "Mild" },
            { "min": 5, "max": 6, "label": "Moderate to Severe" }
          ]
        }
      }
    }
  },
  "translations": {
    "en": {
      "eas_head":      "Over the <b>past week</b>, how often have you experienced the following?",
      "eas_never":     "never",
      "eas_sometimes": "sometimes",
      "eas_often":     "often",
      "eas_inst":      "This short questionnaire asks about feelings of anxiety you may have experienced recently. There are no right or wrong answers.",
      "eas1":          "Felt nervous, anxious, or on edge",
      "eas2":          "Been unable to stop or control worrying",
      "eas3":          "Felt a sense of dread about what might happen",
      "debrief":       "Thank you for completing the Example Anxiety Screen."
    }
  }
}

14. Versioning & Compatibility Notes

  • scale_info.version tracks the version of the individual scale definition file (e.g. when item wording or scoring is corrected), not the OSD format version.
  • The OSD format itself is at version 1.x. Future versions will maintain backwards compatibility: new optional keys may be added, but existing required keys will not be removed or renamed in a 1.x release.
  • Unknown top-level keys are silently ignored by compliant parsers. This means implementations can add custom extension keys (e.g. "_notes") without breaking other runners.
  • Minimum required keys for a valid definition: scale_info (with name and code), likert_options (or null), dimensions, items, and scoring.
  • Validation tool: Run python3 tools/validate_scale.py scales/YOURCODE/ to check that all required keys are present, all text_key values have translation entries, and all scoring item references resolve to valid item ids.

Format Version History

Version Date Changes
1.0 2026-02-15 Initial specification
1.0.1 2026-02-17 C9 rewritten: flat multi-constraint validation format; added min_words/max_words; per-constraint {field}_error keys
1.0.2 2026-02-17 C5 rewritten: sections replace pages; "type": "section" inline marker model; random_group clarified as section-scoped; randomize_pages renamed randomize_sections
1.0.3 2026-02-18 "questions" array key renamed to "items" (questions retained as alias); C4a added: media embedding via <img> in item text; section type added; S1 condition key "question" renamed to "item"
1.0.4 2026-02-18 C4a: remote media sourcing policy — images block remote URLs by default; remote="true" attribute opts in per tag
1.0.5 2026-02-19 C5: revisable and randomize promoted to named section fields; S4: per-section randomize takes priority over scale-level shuffle_questions for that section
1.0.6 2026-02-22 C3: added max and min scoring methods; added scores field for hierarchical scoring (subscale → total); added evaluation-order requirement; added QIDS example
1.0.7 2026-02-25 C3: added sd scoring method; added transform field — optional sequence of affine steps applied to the raw score after the scoring method
1.0.8 2026-02-26 C2: added question_head per-item override; multi/multicheck options accept plain strings as shorthand; option value may be a number
1.0.9 2026-03-01 C2: added likert_reverse boolean — displays response buttons in descending order; stored value is unchanged
1.0.10 2026-03-03 C3: added weighted_mean scoring method; weights field applies to both weighted_sum and weighted_mean; C2: grid adaptive rendering note for narrow screens
1.0.11 2026-03-19 C3: added answer_categories for named answer-category sets, enabling multiple sum_correct dimensions against different answer sets
1.0.12 2026-04-01 C1: added implementation object — metadata about who created the .osd file and licensing for the digital implementation (distinct from scale content license); fields: author, organization, date, license, license_url, notes