← Documentation

The sighting file

One recording is one JSON file, whether the editor wrote it or you did. This is what it can hold, and what each field means.

A recording is a plain JSON file. Nothing in it is a binary blob, an id into a database, or a reference to this site — you can write one by hand, generate one from your own archive, or diff two of them in a code review.

Every excerpt below is read-only, and each knows the format: put the caret inside an object and press Ctrl+Space (+I on a Mac) to list every key that could go there, with what the model says of each.

The observation#

FieldMeaning
versionAlways 1
idWhich testimony this is, unique across every recording anywhere: the day, then who saw it ("1964-04-24-ZamoraLonnie"), or where for an anonymous witness ("1964-04-24-Socorro"). What a case names it by
time, endTime{ year, month, day, hour, minute, second, raw }, every part optional — that is how the format states “1954” or “around 05:00”. raw is the date as written in EDTF, and it is what the date means: "1948-07-24T02:45~" (approximate), "2025-06?" (uncertain), "1965-07-01%" (both), "19XX" (a masked year), or "05:00" alone for a time of day remembered without its date. The numbers are kept in step with it for what computes (the sky, the clock). It is a subset of EDTF (level 0, these qualifiers on the whole date, masked years); @rr0/time is RR0's full EDTF model, into which UFO@home's own tooling converts a recording's dates
durationSecondsAn alternative to endTime, and it wins if both are given
utcOffsetHoursThe LEGAL time the witness's clock was on (+1 for France in 1965). Absent means it is approximated from the longitude, which cannot know legal time or a daylight-saving switch
place[{ lat, lng, name }]name is the fully qualified place name the coordinates were resolved from
witness{ id, title, lastName, firstNames }, all optional; omit entirely for an anonymous witness. id is a reference to the person (on RR0, their directory: "ZamoraLonnie"); the other fields describe them when nobody has given them one
descriptionThe account in prose — one string, or one per language (see below)
tagsA list of strings, written in English: they are technical terms, and two recordings that share one have to match on it. Each reader is shown them in their own language where a translation is known

Several witnesses: the case#

Each witness has a recording of their own, and a recording does not say which case it belongs to: a testimony stands on its own. What shows them together is the case, which names them: the case.json of an RR0 dossier, which states the case's title, date and classification, and lists everything that happened in it as events. Its events of type sighting are its testimonies, each pointing at one witness's recording:

{
  "id": "ChilesWhitted",
  "title": "Chiles et Whitted",
  "time": "1948-07-24 02:45",
  "events": [
    { "type": "event", "eventType": "sighting", "url": "witness-chiles.json" },
    { "type": "event", "eventType": "sighting", "url": "witness-whitted.json" }
  ]
}
FieldMeaning
idThe case's own identifier. On rr0.org it is the dossier's directory and may be left out; a case file standing alone states it
title, timeThe case's name, and when it happened as RR0 writes a time ("1948-07-24 02:45", "1954"). The player names a case it opens by its title
eventsThe case's chronology. Only the sighting ones are replayed, each by its url, read relative to the case file's own address (so the same case works from its dossier's page and from anywhere else); the others (an analysis, an article, a film, a confession) are RR0's

Give it to <rr0-sighting src> or to the player, and each witness can be picked from a list. One recording can be given directly, with no case, but a case with one sighting works the same way and names what it shows. Try it with case-chiles-whitted.json (play it).

Saying it in more than one language#

A recording is handed from one reader to another, so every field an author writes can hold one string per language instead of one: description, a shape's or a decor object's title, and a milestone's label and note.

{
  "description": {
    "fr": "Tout le témoignage de Lonnie Zamora, d'un seul tenant…",
    "en": "Lonnie Zamora's whole testimony, of a piece…"
  }
}

Keys are language tags as a browser gives them (fr, en, pt-BR), and none of them is required. A plain string stays perfectly valid and means “in whatever language it was written in” — which every recording made before this is. A reader whose languages are none of the ones present gets what the file DOES have rather than an empty field: a missing translation must never turn something the witness said into something they did not.

Which language a reader gets is their browser's, unless the page says otherwise: a lang on the element itself, or on anything around it, is taken first — an article that declares its own language has already stated what language its reader is reading it in. The browser's list is what follows, so declaring one forces a choice without throwing away the others.

The editor shows one language, the reader's own, and writing back touches only that one — so opening a file in the other language and typing is how a translation gets added, and one author cannot delete another's.

What was seen#

timeline.keyframes is a list of { t, shapes }, t in milliseconds from the start. Each shape carries a sourceId — several shapes can share one timeline (the phenomenon, a trailing flame, a second light) — and a shape:

{
  "kind": "oval",
  "bounds": { "x": 0, "y": 0, "width": 0, "height": 0 },
  "color": "#39ff14",
  "angle": 0,
  "transparency": 0,
  "haloScale": 1.5,
  "brightness": 0,
  "blur": 0,
  "selected": false,
  "title": "the phenomenon",
  "angular": { "widthDeg": 1.2, "heightDeg": 0.4 }
}
FieldMeaning
kindoval, or polygon, which then also takes points
colorAny CSS colour
angleRadians
transparency0 opaque to 1 invisible
haloScaleThe glow around it; 0 is none
brightnessHow dazzling: a veil, aperture spikes, a core clipped to white
blurHow indistinct the witness said the edges looked
angularIts apparent size in degrees — see below

angular is the authority. bounds is that angle projected onto the fixed 640×360 canvas at the pose's own field of view and through the recording's own instrument; it is re-derived on load, so a file survives a change of canvas, of field of view or of instrument. If the two ever disagree, the angle wins.

timeline.order is the back-to-front paint order, timeline.groups the grouped source ids. Both optional.

Everything around it#

FieldMeaning
witnessTrack{ keyframes: [{ t, pose }] }pose holds lat, lng, elevationM (above the local ground), headingDeg, pitchDeg, rollDeg, fovDeg, and for a camera fNumber and focusDistanceM
weatherTrack{ keyframes: [{ t, weather }] } — the sky's conditions along the recording: precipitation, wind, storm, and the clouds as layers with real heights, each able to hold individual clouds placed in metres. Every field of a weather is in the next section
weatherSource{ id, name, url } of the record the weather was looked up from. Its presence means the recording is replayed exactly as authored and never looked up again. Absent means the witness's own account
soundTrack{ keyframes: [{ t, sound }] }kind (none/hum/whistle/rumble/crackle), volume, pitchHz, optional src of a real recording
referencesPictures of the place laid over the scene: src (an address, or a data: URL for a picture added from a disk), kind (photo/panorama), registration (headingDeg, pitchDeg, rollDeg, fovDeg), opacity, credit/creditUrl, optional t and drawing, and the landmarks it was lined up on (id, label, picture as { u, v } from the top-left corner, scene as { azimuthDeg, altitudeDeg })
instrument, exposureSecondsWhat it was observed through, and how long the shutter was open. Absent means the naked eye
decorScenery at a real eastM/northM from the witness: buildings (with floors, windows), trees, streetlights, vehicles, other witnesses, aircraft — optionally with a track and lights whose pattern carries a real flash rate

What it was: interpretations#

A recording states angles, and a body in metres is never part of what was seen. It is a claim about it, and it is tested by standing it in the scene and looking at it from where the witness stood: it casts its shadow, the ground can hide it, and its outline is measured against what the witness said at every instant. An interpretation is shown alone, as the world it claims; asked to compare (the ◌ button, or compare-testimony on <rr0-sighting>), the player draws everything the witness saw beside it as dashed outlines and lists how far off the direction is and how many times wider and taller each body looks, in red when a witness could not have been that far off.

The witness's own reading goes in the recording, as interpretation. An analyst's goes in the case, as an event of type interpretation naming the recording by its id, with who claims it in by ({ "people": id }, { "org": id }, or a person described in value) and its bodies inline or in a file at url. A testimony whose witness said what it was is drawn in the round, as they said; one that says nothing in metres is drawn as the angles it states. The player offers it and each analyst's interpretation, one at a time.

"interpretation": {
  "title": "A craft standing on its legs",
  "bodies": [{
    "id": "craft",
    "explains": ["ufo-1"],
    "model": { "id": "ellipsoid" },
    "track": [
      { "t": 52000, "eastM": -571.6, "northM": -965.5, "onGround": true,
        "sizeM": { "widthM": 3.36, "lengthM": 3.36, "heightM": 1.73 },
        "appearance": { "color": "#e8e6df", "albedo": 0.7 } },
      { "t": 83000, "azimuthDeg": 195.9, "altitudeDeg": 4.1, "distanceM": 44 }
    ]
  }]
}
FieldMeaning
explainsThe sourceIds of the phenomena this body claims to be
modelA shape built here (ellipsoid, sphere, disc, cylinder, cone, box, torus), a model of the catalogue by id, or a glTF file at url with its credit. Stretched to sizeM whichever it is
trackWhere it is and what it looks like at each t. A position is stated either in the world (eastM/northM from where the witness stood at the start, like the decor, with onGround or altitudeAboveGroundM) or from the witness at that instant (azimuthDeg, altitudeDeg, distanceM). A body onGround stands on the relief; a direction with no distance then meets the ground where that line does. sizeM, attitude (headingDeg, pitchDeg, rollDeg) and appearance (color, albedo) hold until a later keyframe restates them. A flame (lengthM, widthM, color at the nozzle, tipColor, luminanceCdM2) is lit at the keyframe that states it, comes out of the model's node named exhaust (or the one its node names), lights what is around it, raises dust where it meets the ground when raisesDust says so, and is put out by a luminanceCdM2 of 0
outlineNodeThe node of the model that is what the witness drew ("hull" for a craft whose legs are not in the drawing): what its outline is measured by
smokeOn the interpretation itself: what it sets burning on the ground, as { eastM, northM, fromT, untilT? }, seen by its smoke carried off by the recording's wind

The weather, and its clouds#

A weather keyframe states the sky's conditions at one moment of the recording's clock; between two keyframes every number is blended, and precipitation type and storm are held. It carries:

FieldMeaning
cloudLayersThe clouds, as a list of layers — see below. Absent means the older fields on this row's neighbours describe them, and they are adapted into one water layer and one cirrus veil; an empty list means a clear sky somebody looked at
cloudCover, lowerCloudCover, highCloudCoverFractions of sky (0–1): the total, the water decks alone, and the icy veil alone. Written by recordings made before there were layers, and still kept in step by the editor as a summary of them
cloudBaseM, cloudDarknessThe same era's one base, in metres above the reference ground, and one shade (0 white, 1 very dark)
iceCrystalAlignment0–1, how steadily the ice crystals fell — what turns a bare ring into sundogs, arcs and a pillar. No record measures it; a cirrus layer carries its own
relativeHumidity0–1, near the ground. It decides how milky the clear sky is: haze swells with water as the air nears saturation. A looked-up record carries it (from ERA5's temperature and dew point); absent means a typical haze
precipitationType, precipitationIntensitynone/rain/snow/hail, and 0–1
windDirectionDeg, windSpeedThe general wind: the bearing it blows TOWARD, clockwise from north, and metres per second. It is what carries the clouds — from time zero, so seeking and replaying give the same sky
stormLightning and thunder, at the right delay

Each layer of cloudLayers is a deck of clouds at a real height, and stays itself from one keyframe to the next:

FieldMeaning
idStable across keyframes — layers are matched by it, never by position in the list. A layer present in one keyframe and absent from the next fades out; reordering them changes nothing
typecumulus, stratus, stratocumulus, cirrus or unknown. It decides the shape of the tops and how thin the veil is; a cirrus is also the one that refracts haloes. It switches at the keyframe, it is not blended
baseM, thicknessMMetres. The base is above the recording's REFERENCE ground, not above a witness who climbs; a witness above the base is inside or over the deck, and the sky is drawn accordingly
coverage0–1, and it means what it says: the fraction of the sky this layer covers, whatever the size of its clouds
sizeMThe characteristic width of one cloud, in metres. Separate from coverage: the same fraction of sky can be many small clouds or a few large ones
density0–2, how opaque the cloud matter is; 0 is transparent. Separate from coverage too
darkness0 white to 1 very dark. Absent means the keyframe's cloudDarkness
seedWhich pattern, out of the endless ones the same numbers can draw. Absent means one derived from the id, which is why the id must not change
windDirectionDeg, windSpeedThis layer's own wind, when it differs from the general one — the high deck usually does. Absent means the general wind
iceCrystalAlignmentFor a cirrus only
instancesIndividual clouds inside this layer — see below

An individual cloud in instances is one cloud of its layer that the file places exactly, because the account did: the one the phenomenon went behind, the one that was there and nowhere else. It is drawn as one of its layer's own — the same texture, the same threshold — told apart from its neighbours by nothing but where it stands and how big it is, and it stands even when the layer's coverage is nought. It rides the layer's wind like the rest, and it hides a phenomenon it passes in front of.

FieldMeaning
idStable across keyframes, same rule as a layer's
eastM, northMWhere its centre was at time zero, in metres from the witness's starting point. The wind carries it from there
baseM, thicknessMIts own base and height, metres — a cloud can sit lower or stand taller than its deck
widthM, depthM, rotationDegIts footprint, metres, and the bearing that footprint is turned to
density, darknessIts own; darkness absent means the layer's
{
  "weather": {
    "cloudLayers": [
      {
        "id": "low", "type": "cumulus",
        "baseM": 1500, "thicknessM": 800,
        "coverage": 0.55, "sizeM": 1400, "density": 1, "darkness": 0.15,
        "instances": [
          { "id": "the-one", "eastM": 0, "northM": 4200,
            "baseM": 1500, "thicknessM": 800,
            "widthM": 1900, "depthM": 1300, "rotationDeg": 12, "density": 1 }
        ]
      },
      { "id": "high", "type": "cirrus", "baseM": 8000, "thicknessM": 400,
        "coverage": 0.2, "sizeM": 2200, "density": 0.35, "iceCrystalAlignment": 0.65 }
    ],
    "precipitationType": "none", "precipitationIntensity": 0,
    "windDirectionDeg": 90, "windSpeed": 5, "storm": false
  }
}

A recording whose weather was looked up (it has a weatherSource) holds the record's answer, not a link to it: ERA5 gives the low, middle and high bands as three layers named record-low, record-mid and record-high, the low base estimated from the spread between temperature and dew point, the other two at 3 500 m and 8 000 m. Their type is unknown (cirrus for the high one), their size and density are drawing assumptions: a reanalysis knows how much of each band was covered, not what the clouds looked like. Ask the record again from the editor and the layers are rewritten; edit a layer by hand and the recording becomes the author's, the source dropped.

A whole file#

The smallest recording that still states something — one silent oval crossing the sky over twelve seconds, on a real date at a real place. Everything else in the format is optional, and everything below is doing work:

{
  "version": 1,
  "description": "The smallest recording that still states something: one silent orange oval, a degree and a half wide, crossing the south-western sky from left to right over twelve seconds.",
  "time": { "year": 2024, "month": 8, "day": 12, "hour": 22, "minute": 30 },
  "durationSeconds": 12,
  "utcOffsetHours": 2,
  "place": [{ "lat": 45.19, "lng": 5.72, "name": "Grenoble, Isère, France" }],
  "timeline": {
    "keyframes": [
      {
        "t": 0,
        "shapes": [
          {
            "sourceId": "ufo-1",
            "shape": {
              "kind": "oval",
              "bounds": { "x": 150, "y": 150, "width": 10, "height": 3 },
              "angular": { "widthDeg": 1.6, "heightDeg": 0.5 },
              "color": "#ff8c1a",
              "angle": 0,
              "transparency": 0,
              "haloScale": 1.2,
              "selected": false
            }
          }
        ]
      },
      {
        "t": 12000,
        "shapes": [
          {
            "sourceId": "ufo-1",
            "shape": {
              "kind": "oval",
              "bounds": { "x": 480, "y": 130, "width": 10, "height": 3 },
              "angular": { "widthDeg": 1.6, "heightDeg": 0.5 },
              "color": "#ff8c1a",
              "angle": 0,
              "transparency": 0,
              "haloScale": 1.2,
              "selected": false
            }
          }
        ]
      }
    ]
  },
  "witnessTrack": {
    "keyframes": [
      {
        "t": 0,
        "pose": { "lat": 45.19, "lng": 5.72, "elevationM": 0, "headingDeg": 215, "pitchDeg": 20, "fovDeg": 60 }
      }
    ]
  },
  "soundTrack": { "keyframes": [{ "t": 0, "sound": { "kind": "none", "volume": 0, "pitchHz": 100 } }] }
}

To change it and see it play, paste it into the player, whose editor completes on every key the format has, offers the words each one accepts, and says what the model says about it.

It is /demo-data/example-minimal.json on this site, so you can fetch it, and play it before changing anything. Note that angular and bounds both appear: the angle is what the file MEANS, and the pixels are re-derived from it on load — write the angle, and let a wrong guess at the pixels be corrected for you.

Larger ones to read#

Every demo on this site is a plain file you can open. These four are the ones worth reading to see how a real recording is put together:

FileWhat to look at in it
witness-chiles.jsonA real case: a witness, a case id shared with a second recording, ten keyframes, a looked-up weatherTrack with its weatherSource
sky-test-halos.jsonNo phenomenon at all — a sky set up by a weatherTrack whose keyframes change the crystals' alignment, the cirrus cover and a cumulus deck, watched through a witnessTrack that pans across the display and then holds
sky-test-clouds.jsonThree cloud layers with metre-based altitude, thickness, size, density and wind, evolving on the weather timeline — and in the first one an instances entry: one cloud of the field, placed and sized in metres, that grows and darkens over the two minutes
sky-test-aircraft.jsonAn instrument and an exposureSeconds, and a decor aircraft with a track and seven lights at their real flash rates
instrument-instamatic.jsonThe same sighting as witness-socorro.json, changed in one field. Diff the two

Four rules that decide what a file means#

  • Discrete fields are held, continuous ones are blended. A shape left out of a later keyframe stays as it was; one whose first keyframe is at five seconds is already painted, in that state, from zero. To make something stop being visible, keyframe it at transparency: 1.
  • Angles only. No real size and no real distance is stored anywhere. Metres are derived, as inequalities, from what the phenomenon was stated to pass behind or in front of (decor[].occludesSourceIds).
  • Declared outranks deduced. occludesSourceIds records statements by the witness. Nothing in this format can deduce them: it describes an appearance on a field of view, not a position in space.
  • Absent is not zero. No sound track means nobody was asked; kind: "none" means the witness reported hearing nothing. The same distinction runs through the weather and the ice cloud.

The README carries the full field-by-field reference, including the reasoning behind each choice, and is the canonical source if this page and it ever disagree.