# Target marks

The holes, the aim point and the scale marked on a photo of the target for one logged group, from which its centre, size and shot count are worked out.

## Generated table

**Record:** observation · **Covers:** `marks`

| Field | Type | Required | Limits in the schema | Solver reads | In a shared log |
|---|---|---|---|---|---|
| `marks.aim` | object | yes, within | - | no | as stored |
| `marks.aim.x` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.aim.y` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.bull` | number | yes, within | min 1; max 20 | no | as stored |
| `marks.holes` | array of object | yes, within | at most 100 items | no | as stored |
| `marks.holes[].flyer` | `'true'` | no | - | no | as stored |
| `marks.holes[].shot` | number | no | min 1; max 100 | no | as stored |
| `marks.holes[].x` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.holes[].y` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.image` | object | yes, within | - | no | as stored |
| `marks.image.h` | number | yes, within | min 1; max 2000 | no | as stored |
| `marks.image.w` | number | yes, within | min 1; max 2000 | no | as stored |
| `marks.photo` | string | no | pattern ^asset:[0-9a-f]{64}$ | no | removed when notes are hidden or photos are off |
| `marks.scale` | object | yes, within | - | no | as stored |
| `marks.scale.a` | object | yes, within | - | no | as stored |
| `marks.scale.a.x` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.scale.a.y` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.scale.b` | object | yes, within | - | no | as stored |
| `marks.scale.b.x` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.scale.b.y` | number | yes, within | min 0; max 2000 | no | as stored |
| `marks.scale.length` | number | yes, within | max 1000 | no | as stored |
| `marks.scale.source` | `'typed'` or `'saved'` or `'detected'` | yes, within | - | no | as stored |
| `marks.scale.unit` | `'in'` or `'cm'` | yes, within | - | no | as stored |
| `marks.sheet_id` | string | yes, within | - | no | as stored |

## What it holds

A group can carry the holes marked on a photo of its target: where each hole is on the photo, where the shooter aimed, and two points a known distance apart that give the photo its scale. Each hole can be flagged as a flyer and numbered in the order it was fired. The results (the group's centre, its size, its mean radius and its spread up-down and left-right) are not stored here: they are worked out from the marks every time. When the marks are saved, the group's own centre, size and shot count are written from them. An absent `marks` means the group was not marked, which is every group before 2026-10-16.

## What to know first

**The marks replace the typed centre, size and count.** Saving marks writes `group.center_up_mil`, `group.center_right_mil`, `group.size_mil` and `group.n_shots` from them; the editor then shows those as "From the marked target" and has no boxes for them.

**Flyers count for nothing but the tally.** A flagged hole is drawn and stored, and left out of every number.

**Pixels and inches, not angles.** The holes are photo pixels; the scale turns them into inches; the group's range turns inches into mil. Changing the range keeps the inches and works the angles out again.

**One photo, several bulls.** Groups marked on one sheet share its photo and a sheet id, each with its own bull number and aim point.

## The fields

**`marks.photo`** The reference to the target photo, `asset:` and the SHA-256 of the picture's bytes, like the camera-aim photo's. The picture is drawn on the phone at most 2,000 pixels on its long side and re-encoded, so it carries no camera metadata. The app always writes it; a reader's copy of a shared log may lack it (see Sharing and privacy).

**`marks.image`** The stored picture's size: **`marks.image.w`** its width and **`marks.image.h`** its height, in pixels, 1 to 2000. Every point below lies within them.

**`marks.sheet_id`** The same text on every group marked on one photo; up to 40 characters. Example: a random identifier made when the first bull is marked.

**`marks.bull`** Which bull on the sheet, 1 to 20. The first group marked on a photo is bull 1; "Next bull on this sheet" gives the next number.

**`marks.scale`** The scale: two points on the photo a known distance apart on the paper, and that distance.

**`marks.scale.a`** and **`marks.scale.b`** The two points: **`marks.scale.a.x`**, **`marks.scale.a.y`**, **`marks.scale.b.x`** and **`marks.scale.b.y`**, in pixels from the photo's top-left corner (0 to 2000, `y` downwards). They must be at least 10 pixels apart for a result.

**`marks.scale.length`** The distance between them on the paper, above 0 and at most 1000. **`marks.scale.unit`** `'in'` or `'cm'`. **`marks.scale.source`** How the scale was given: `'typed'` today; `'saved'` and `'detected'` are kept for a saved target type and automatic detection, which are not built.

**`marks.aim`** Where the shooter aimed: **`marks.aim.x`** and **`marks.aim.y`**, in pixels. Up and right of it are plus, as for the group's centre.

**`marks.holes`** Up to 100 holes. Each has **`marks.holes[].x`** and **`marks.holes[].y`**, its centre in pixels; **`marks.holes[].flyer`**, `true` when the shooter called it a flyer (absent otherwise); and **`marks.holes[].shot`**, the order it was fired, 1 to 100, when known.

**What is worked out from them.** At the group's range: the centre (the mean of the kept holes, in inches and mil), the size (the largest distance between two kept holes, centre to centre), the mean radius (the mean distance from the centre) and the spreads (sample standard deviations up-down and left-right, with n − 1). With one kept hole there is a centre and no size, radius or spread; with none there is no result, and the app refuses the save. The angle is exact, the arctangent of the inches over the range, so 1 in at 100 yd is 0.278 mil.

## Who writes it

**The app.** A group's row in the range log has "Mark target" (or "Edit marks") in its Edit or delete menu. The marking screen asks for a photo, then two scale points and the length between them, then the aim point, then the holes, and shows the result as the holes go on. Save reads the group again from the device, writes the marks and the group's centre, size and shot count, and pushes the group like any edit; "Remove marks" takes the block away and leaves the centre, size and count as they were. After a save, "Next bull on this sheet" offers this log's other groups of the same profile with no marks; the chosen one opens on the same photo and scale. The saved-group editor keeps the marks through an edit and works the centre, size and count out again at the group's range.

**Not written by** the connector, the fitter or the server.

## Who reads it

**The app.** A marked group has a line under its row ("Marked: 3 shots · group 0.42 mil · 0.10 mil high, 0.05 mil left"), in the scope's unit, with View, in your own log and in a log shared with you. The fitter reads the group's centre and size, as for any group; it does not read the marks themselves.

**The connector.** `get_session` returns a marked group without its `marks` and with `marks_result` (the numbers above, to three decimals; on an MOA scope each angle also in MOA, since 2026-10-16) and `marks_photo` (whether the block names a photo).

## Why it is stored

The marks are the evidence behind a group's centre and size: kept, they can be checked, re-marked, shown to a reader and, later, used to weigh a group by how tight it was. The results are not stored, so they can never disagree with the marks.

## Changing it

An edit of the group keeps the marks. Changing its range keeps the inches and changes the mil. Re-marking writes the block again; removing it leaves the group's last centre, size and count.

## Sharing and privacy

The holes, the aim point, the scale, the sheet and the bull reach every reader of a shared log as stored: they are pixels on a picture and say nothing about where the shooter was. The photo goes only where notes and photos both go: with notes hidden, or the share's Photos switch off, `marks.photo` is removed and the reader sees the holes on a 1-inch grid. The location switch does not touch it. A reader opens the photo through the share, never from the owner's device.

---

[All reference pages](../index.md)
