Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Open Coaching Format (OCF)

**An open standard for representing sports coaching diagrams, drill animations, and playbooks as structured data.**
**An open standard for representing team-sport coaching diagrams, drill animations, and playbooks as structured data — for invasion team sports, basketball first.**

[![License: CC BY 4.0](https://img.shields.io/badge/License-CC%20BY%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)
[![Schema Version](https://img.shields.io/badge/schema-v1.1.1-blue)](schema/v1.json)
[![Schema Version](https://img.shields.io/badge/schema-v1.2.0-blue)](schema/v1.json)
[![Status: Draft](https://img.shields.io/badge/status-draft-yellow)]()

---

## What is OCF?

The Open Coaching Format is a JSON-based standard for encoding sports coaching content — drills, plays, playbooks, and animated diagrams — in a structured, interoperable format.
The Open Coaching Format is a JSON-based standard for encoding team-sport coaching content — drills, plays, playbooks, and animated diagrams — in a structured, interoperable format. It targets **invasion team sports** (basketball, soccer, handball, hockey, futsal), with **basketball fully defined first** and the others reserved with provisional vocabularies.

**No open standard for this currently exists.** All major coaching tools (FIBA Europe Coaching App, FastDraw, Sportplan.net) use proprietary formats with image-only export. OCF changes that.

Expand Down
31 changes: 30 additions & 1 deletion docs/specification-v1.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
= Open Coaching Format — Open Standard for Basketball Drill Diagrams
:version: 1.1.1
:version: 1.2.0
:doctype: book
:toc: left
:toc-title: Table of Contents
Expand Down Expand Up @@ -527,6 +527,28 @@ Rendered as a numbered marker with optional description. Used for circuit traini

---

== Sport

OCF targets **invasion team sports** — two teams sharing a field, ball/puck
possession, and off-ball movement (basketball, soccer, handball, hockey,
futsal). Basketball is fully defined; the others are reserved with provisional
action vocabularies.

The optional `sport` field declares the sport:

[source,json]
----
"sport": "basketball"
----

`sport` is optional and defaults to `basketball` for backwards compatibility;
it is planned to become required in v2. The declared sport determines which
action types (and variants) are valid — e.g. `screen`/`rebound` are valid under
`basketball`, `tackle`/`clear` under `soccer`. Documents with no `sport` field
are treated as basketball.

---

== Actions

Movement and ball events are expressed as semantic *actions*, not geometric
Expand Down Expand Up @@ -1413,6 +1435,13 @@ npx ajv-cli validate \
[appendix]
== Changelog

=== v1.2.0

Adds the optional `sport` field and per-sport action-type scoping (RFC 0003).
Basketball is fully defined; soccer/handball/hockey/futsal are reserved with
provisional vocabularies. Additive and backwards-compatible — documents without
a `sport` field validate as basketball.

=== v1.1.1

Tooling and distribution fixes only; no schema shape change. Publishes the
Expand Down
7 changes: 7 additions & 0 deletions examples/invalid/sport-basketball-tackle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sport": "basketball",
"meta": { "id": "d4e5f6a7-4444-4a6e-8f0c-2d5e9a1b3c7f", "title": "Tackle under basketball" },
"court": { "ruleset": "fiba", "type": "half_court" },
"entities": [ { "type": "defense", "nr": 4, "x": 0, "y": 8 } ],
"frames": [ { "id": "f", "actions": [ { "player": "defense_4", "type": "tackle" } ], "end_state": {} } ]
}
7 changes: 7 additions & 0 deletions examples/invalid/sport-soccer-screen.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sport": "soccer",
"meta": { "id": "e5f6a7b8-5555-4a6e-8f0c-2d5e9a1b3c7f", "title": "Screen under soccer" },
"court": { "ruleset": "custom", "type": "full_court", "custom_dimensions": { "unit": "m", "length": 105, "width": 68, "basket_from_baseline": 0, "three_point_distance": 0, "paint_width": 40.3, "paint_depth": 16.5, "free_throw_distance": 11 } },
"entities": [ { "type": "offense", "nr": 4, "x": 0, "y": 8 } ],
"frames": [ { "id": "f", "actions": [ { "player": "offense_4", "type": "screen", "for_player": "offense_4" } ], "end_state": {} } ]
}
6 changes: 6 additions & 0 deletions examples/invalid/sport-unknown.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sport": "volleyball",
"meta": { "id": "c3d4e5f6-3333-4a6e-8f0c-2d5e9a1b3c7f", "title": "Unknown sport" },
"court": { "ruleset": "fiba", "type": "half_court" },
"entities": [], "frames": [ { "id": "f", "actions": [], "end_state": {} } ]
}
16 changes: 16 additions & 0 deletions examples/sport-basketball.ocf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://opencoachingformat.org/schema/v1.json",
"sport": "basketball",
"meta": { "id": "a1b2c3d4-1111-4a6e-8f0c-2d5e9a1b3c7f", "title": "Sport-tagged basketball", "created": "2026-08-27T10:00:00Z", "source_format": "open" },
"court": { "ruleset": "fiba", "type": "half_court", "drill_focus": "offense" },
"entities": [
{ "type": "offense", "nr": 1, "x": 0.0, "y": 5.68 },
{ "type": "offense", "nr": 4, "x": -2.45, "y": 8.2 }
],
"balls": [ { "id": "ball_1", "carried_by": "offense_1" } ],
"frames": [
{ "id": "f1", "label": "Screen", "description": "big sets a ball screen",
"actions": [ { "player": "offense_4", "type": "screen", "for_player": "offense_1", "variant": "ball_screen" } ],
"end_state": { "offense_1": { "named": "top_of_the_key" }, "offense_4": { "x": -0.5, "y": 6.7 } } }
]
}
16 changes: 16 additions & 0 deletions examples/sport-soccer.ocf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://opencoachingformat.org/schema/v1.json",
"sport": "soccer",
"meta": { "id": "b2c3d4e5-2222-4a6e-8f0c-2d5e9a1b3c7f", "title": "Sport-tagged soccer (provisional)", "created": "2026-08-27T10:00:00Z", "source_format": "open" },
"court": { "ruleset": "custom", "type": "full_court", "custom_dimensions": { "unit": "m", "length": 105, "width": 68, "basket_from_baseline": 0, "three_point_distance": 0, "paint_width": 40.3, "paint_depth": 16.5, "free_throw_distance": 11 } },
"entities": [
{ "type": "offense", "nr": 9, "x": 0.0, "y": 30.0 },
{ "type": "defense", "nr": 4, "x": 0.0, "y": 40.0 }
],
"balls": [ { "id": "ball_1", "carried_by": "offense_9" } ],
"frames": [
{ "id": "f1", "label": "Tackle", "description": "defender challenges",
"actions": [ { "player": "defense_4", "type": "tackle" }, { "player": "offense_9", "type": "pass", "to_player": "offense_9" } ],
"end_state": { "offense_9": { "x": 0.0, "y": 30.0 }, "defense_4": { "x": 0.0, "y": 38.0 } } }
]
}
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@opencoachingformat/spec",
"version": "1.1.1",
"version": "1.2.0",
"type": "module",
"description": "Open standard for basketball drill diagrams and animations.",
"description": "Open standard for team-sport coaching diagrams and animations (invasion team sports; basketball first).",
"repository": {
"type": "git",
"url": "git+https://github.com/opencoachingformat/spec.git"
Expand All @@ -15,6 +15,7 @@
"files": [
"schema",
"positions",
"sports",
"README.md",
"LICENSE"
],
Expand All @@ -24,7 +25,12 @@
"./positions/fiba-v1.json": "./positions/fiba-v1.json",
"./positions/nba-v1.json": "./positions/nba-v1.json",
"./positions/ncaa-v1.json": "./positions/ncaa-v1.json",
"./positions/nfhs-v1.json": "./positions/nfhs-v1.json"
"./positions/nfhs-v1.json": "./positions/nfhs-v1.json",
"./sports/basketball-v1.json": "./sports/basketball-v1.json",
"./sports/soccer-v0.0.1.json": "./sports/soccer-v0.0.1.json",
"./sports/handball-v0.0.1.json": "./sports/handball-v0.0.1.json",
"./sports/hockey-v0.0.1.json": "./sports/hockey-v0.0.1.json",
"./sports/futsal-v0.0.1.json": "./sports/futsal-v0.0.1.json"
},
"publishConfig": {
"access": "public"
Expand All @@ -33,7 +39,8 @@
"validate": "ajv validate --spec=draft7 -s schema/v1.json -d \"examples/*.ocf.json\" --all-errors --verbose -c ajv-formats",
"test:invalid": "node scripts/check-invalid.mjs",
"test:positions": "node --test test/positions-anchors.test.mjs",
"test": "npm run validate && npm run test:invalid && npm run test:positions"
"test:sport": "node --test test/sport-branches.test.mjs",
"test": "npm run validate && npm run test:invalid && npm run test:positions && npm run test:sport"
},
"devDependencies": {
"ajv-cli": "^5.0.0",
Expand Down
Loading
Loading