The schema

FH-Ego v1, field by field

This is the whole of what we deliver, one row per field, read out of the code that writes it. 47 fields across three tiers. Where a value has no unit, the table says so rather than leaving the cell empty.

The tables are generated from annotationModel.py, which is the same file the pipeline validates every clip against. There is no second copy of the unit, shape, rate and tier information to drift from this one, and a field added without them fails our build rather than arriving here as a blank cell.

Read this before the tables

The unit of position_3d_m

position_3d_m ends in _m and is not in metres. Its x and y are image-plane fractions rescaled by two constants chosen by hand; its z is a fixed offset plus a hand-relative depth with the sign discarded. We publish no camera intrinsics, so nothing here converts to a distance. Over the clip below, z varies by 0.071 across the whole sixty-one seconds. Read as metres — which the key invites and the code forbids — that is seven centimetres of hand travel in a minute of work. That is the reduction to absurdity: the number is dimensionless, and treating it as a distance produces a false one.

The key keeps its name, because identifiers already in use are not renamed. Its unit is the one stated above.

What confidence is

It is the detector's score, from 0.5 to 1, for its own left-or-right call on each detected hand. The label in hand is set by where the wrist sits in the frame, not by that call. The 21 keypoints carry no score of their own, and the floor is 0.5 because the score chooses between two classes.

Which values are derived

The reference clip lists the fields below as generated rather than observed. They were produced by a template, not a model: its caption_source reads heuristic_stub. Hand keypoints, position, velocity and confidence are derived as well, as the Inside a clip block marks them. The list is:

  • actions[].action
  • actions[].description
  • actions[].scene_semantic
  • actions[].camera_motion
  • actions[].start_frame
  • actions[].end_frame
  • tracks.object_interactions
  • tracks.action_verbs
  • tracks.dense_captions

Tier 1 · Task / session

The clip

One object per delivered clip. Everything else hangs off it.

Inside a clip

The outlined panel marks where a clip's video goes. The labels set out what its FH-Ego v1 annotation records.

Each label lists the fields that hold its signal. Measured values are read from the video file. Derived values are produced by a model or a rule.

Video Measured

Egocentric video of the task. The annotation states its frame count and frame rate.

  • end_frame scalar · no unit
  • fps scalar · hertz

Hands

Each detected hand gets one entry per sampled frame in tracks.kinematics_3d[], stamped with its frame_idx and timestamp_sec. The clip states its own sampling rate in tracks.hand_control.kinematic_sample_rate_hz.

  • 21 hand keypoints Derived

    They mark the wrist and four points along each finger and the thumb. Each point's x and y are fractions of the frame's width and height, and its z is relative to the wrist.

    • keypoints_21 21 x {x, y, z} · no unit
  • 3D hand position and velocity Derived

    Position is the wrist's x, y and z: x and y come from where the wrist sits in the frame, and z from the hand model. Velocity is the change in that position since a hand with the same left or right label was last detected, multiplied by the frame rate.

    • position_3d_m {x, y, z} · no unit
    • velocity_3d_mps {vx, vy, vz} · no unit
  • Per-hand confidence Derived

    The detector scores how sure it is of its own left-or-right call for each hand. The label in hand is set by where the wrist sits in the frame, not by that call.

    • confidence scalar, 0.5–1 · no unit
    • hand scalar

Actions

Each window gets one entry in actions[]. The clip names the captioner that wrote them in provenance.caption_source.

  • Action windows Derived

    The captioner cuts the clip's timeline into windows. Each one records its start frame, its end frame, the hand in use and one verb-noun code, such as grasp_object.

    • start_frame scalar · no unit
    • end_frame scalar · no unit
    • hand scalar
    • action scalar
  • Captions Derived

    Each window carries one caption, and the clip also collects them into one list.

    • description scalar
    • tracks.dense_captions list
13 fields on EgoSchemaDatasetSchema
FieldTypeRequiredUnitShapeRate
task_id string required none scalar once per clip
title string required none scalar once per clip
task_label string required none scalar once per clip
task_name string required none scalar once per clip
details string required none scalar once per clip
scene string required none scalar once per clip
usable bool optional none scalar once per clip
start_frame int required none scalar once per clip
end_frame int required none scalar once per clip
fps int required hertz scalar once per clip
actions array of ActionSegment required none list see the segment fields below
tracks Tracks required none object once per clip
provenance ClipProvenance optional none object once per clip

task_id — Internal identifier for the annotation run

title — Human-readable title for the clip

task_label — Taxonomy code for the task performed

task_name — Readable name of the task performed

details — Free-text description of the session

scene — Environment the capture was made in

usable — Whether the clip passed QA and may be delivered

start_frame — First frame of the clip

end_frame — The clip's frame count. Frames are numbered from zero, so the last frame is end_frame minus one: on the reference clip end_frame is 1843 and the final action window ends at 1842.

fps — Frames per second of the source video

actions — The task-bounded action segments this clip was cut into

tracks — The parallel supervision layers

provenance — Where this clip came from — see the provenance fields below

Tier 1 · Task / session

Where the clip came from

Carried inside the annotation. The hash lets you verify the bytes offline by rehashing them; the consent receipt is the one handle that resolves against our API rather than in your hand.

10 fields on ClipProvenanceModel
FieldTypeRequiredUnitShapeRate
clip_id string required none scalar once per clip
scheme_version string optional none scalar once per clip
content_sha256 string optional none scalar once per clip
cohort string optional none scalar once per clip
captured_at string optional none scalar once per clip
consent_receipt_id string optional none scalar once per clip
qa_verdict string optional none scalar once per clip
qa_reject_reasons array of string optional none list once per clip
derived_fields array of string optional none list once per clip
caption_source string optional none scalar once per clip

clip_id — Canonical FH1 clip identifier; the tail is derived from the clip's SHA-256

scheme_version — Identifier scheme version

content_sha256 — SHA-256 of the capture. Rehash the file to verify this annotation describes the bytes you hold

cohort — Capture cohort

captured_at — When the capture was recorded (partner-declared)

consent_receipt_id — Consent ledger receipt covering this clip; verifiable on request

qa_verdict — Automated QA outcome for this clip

qa_reject_reasons — Closed-vocabulary reject reasons, empty when accepted

derived_fields — Fields on this record that are model-generated, never measured (AGENTS.md 4.1)

caption_source — Which captioner produced the derived caption fields: a model id (e.g. claude-opus-5) or 'heuristic_stub' for development filler. CARRY C.6

Tier 2 · Action segment

One action segment

The clip is divided into fixed windows, and each window is given a verb-noun code. The windows are derived: their length follows from the frame rate, not from what happens in the footage.

7 fields on ActionSegmentModel
FieldTypeRequiredUnitShapeRate
start_frame int required none scalar once per action segment
end_frame int required none scalar once per action segment
hand string optional none scalar once per action segment
scene_semantic string optional none scalar once per action segment
camera_motion string optional none scalar once per action segment
action string required none scalar once per action segment
description string required none scalar once per action segment

start_frame — First frame index of action chunk

end_frame — Last frame index of action chunk

hand — Operative hand: 'left', 'right', or 'both'

scene_semantic — Interacting surface/workstation area

camera_motion — Camera motion classification

action — Standardized verb-noun action code (e.g. scoop_food)

description — Dense natural language sentence describing physical interaction

Tier 3 · Timeline track

The parallel tracks

Supervision layers that run the length of the clip alongside each other.

5 fields on TracksModel
FieldTypeRequiredUnitShapeRate
hand_control HandControlTrack required none object once per clip
object_interactions array of string required none list once per clip
action_verbs array of string required none list once per clip
dense_captions array of string required none list one per action segment
kinematics_3d array of HandKinematicsFrame required none list see the entry fields below

hand_control — Header describing the kinematics track

object_interactions — The noun half of each action code, de-duplicated. Read from the action codes, not from object detection; no object detector runs in the pipeline. Derived, and listed in the clip's derived-fields list.

action_verbs — Distinct action codes in the clip. Derived, and listed in the clip's derived-fields list. When the caption source is a stub, they come from a fixed pool of five codes.

dense_captions — One caption per action window. Derived, and listed in the clip's derived-fields list. The caption-source field names what wrote them; a stub value means a template, not a model.

kinematics_3d — The sampled wrist track, one entry per sampled frame per detected hand

Tier 3 · Timeline track

The kinematics track header

Describes the sampled track below it — including the sample interval, so the file states its own rate.

5 fields on HandControlTrackModel
FieldTypeRequiredUnitShapeRate
hand_dominance array of string required none list once per clip
total_frames_annotated int required none scalar once per clip
kinematic_sample_frames int required none scalar once per clip
kinematic_sample_every_n_frames int optional none scalar once per clip
kinematic_sample_rate_hz float optional hertz scalar once per clip

hand_dominance — Distinct operative-hand codes across this clip's action windows: 'left', 'right' or 'both'. Computed from the action track, not from the hand tracker, so it lists hand codes rather than detected hands.

total_frames_annotated — Frames in the source clip

kinematic_sample_frames — The number of entries in kinematics_3d: one per detected hand per sampled frame, so two hands in view give two entries for one frame. On the reference clip: 294 entries across 185 distinct frames.

kinematic_sample_every_n_frames — Every Nth frame was sampled into kinematics_3d.

kinematic_sample_rate_hz — Derived: clip fps ÷ kinematic_sample_every_n_frames.

Tier 3 · Timeline track

One kinematics sample

One entry per sampled frame per detected hand. This is the layer that carries the detector's output.

7 fields on HandKinematicsFrameModel
FieldTypeRequiredUnitShapeRate
frame_idx int required none scalar once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
timestamp_sec float required seconds scalar once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
hand string required none scalar once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
position_3d_m object of float required none {x, y, z} once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
velocity_3d_mps object of float required none {vx, vy, vz} once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
confidence float required none scalar, 0.5–1 — a two-class score, so 0.5 is the floor, not 0 once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames
keypoints_21 array of object of float optional none 21 x {x, y, z} once per sampled frame per detected hand — the clip states its own interval at tracks.hand_control.kinematic_sample_every_n_frames

frame_idx — Index of the source video frame this sample was taken from

timestamp_sec — Time of this sample from the start of the clip

hand — Which hand this sample describes, set by where the wrist sits in the frame: with two or more hands in view the leftmost is left, and with one hand left of centre is left. The detector's own left-or-right score is in confidence.

position_3d_m — Position of the wrist. x and y are its place in the frame, rescaled by fixed constants; z is a fixed offset plus the hand model's depth for the wrist. No physical unit: the values are not metres, and no camera intrinsics are published.

velocity_3d_mps — The change in position_3d_m since a hand with the same left or right label was last detected, multiplied by the frame rate. Tracking runs on every frame, before sampling. No physical unit: position's units per second, not m/s.

confidence — The hand detector's score, from 0.5 to 1, for its own left-or-right call on this hand. One value per detected hand; the 21 keypoints carry no score of their own. The hand label is set by wrist position, not by this call.

keypoints_21 — 21 MediaPipe hand landmarks, normalised image coordinates [x, y, z] per joint, in MediaPipe's canonical joint order (0 = wrist, 4 = thumb tip, 8 = index tip, … 20 = pinky tip)

What one real clip holds

Numbers below are counted from the published reference clip at build time, not typed.

ClipFH1-DEMO-20260813-4b13ff60
Source video1,843 frames at 30 fps
Action segments16 Derived · fixed four-second windows
Kinematics entries294 across 185 distinct frames
Sample intervalevery 10th frame · 3.0 Hz
Entries with no landmarks0
Consent receiptnone — recorded before the consent flow

The sample interval is recorded on every clip rather than assumed, so a file can always be checked against the rate it claims. A track sampled every tenth frame of thirty is a three-hertz track, and calling it thirty would be the easiest false number on this page to publish.

Open the clip these numbers come from · What arrives, and in what container · Every number we publish, and where it comes from

Check what you downloaded

Every JSON file this site publishes is listed with its SHA-256 in /SHA256SUMS. Fetch both and check them with the tool your machine already has:

curl -sO https://fourthuman.ai/SHA256SUMS
curl -sO https://fourthuman.ai/reference-run.json
sha256sum -c SHA256SUMS        # or on a Mac: shasum -a 256 -c SHA256SUMS

A checksum list, not a signature. It detects accidental change, corruption and damage in transport. The list is served from the same origin as the files it lists, so a matching digest shows that the file you hold is byte-for-byte the file this site serves.