Skip to content
What to ID · BC

What to ID next in BC

Records that needed an ID in British Columbia, picked . See today's records on a map.

Pick a group

Choose a group you know. Open a batch in iNaturalist, ID what you can, then return.

How the test works
Each build
  1. We collectBC records that need an ID and have a photo
  2. We split them into 4 listseach record goes to one list by chance, so the lists hold a similar mix
  3. Each list gets its own orderone is newest first, like iNaturalist today
During the blitz
  1. You press Next batcheach press opens up to 120 records from the next list in turn
  2. You identifyin iNaturalist, as usual
After
  1. We counteach person's species-level IDs on each list, against newest first, and whether the gap is bigger than chance; data-poor places first is judged on IDs weighted by map score

Methods in full

1. Design at a glance

#

Identification is a rate-limiting step in getting records that are already observed to Research Grade, and so to GBIF. The Blitz the Gap authors call for expert identification blitzes and for more informative prioritisation of records to identify [1]. Expert identification blitzes have been run and studied before [2]. This test asks a narrower question: whether the order of records changes what gets identified.

This build has 4 lists. Trial dates and analysis settings are provisional until the BC team finalises the protocol.

Table 1. The design in one table.
PartThis test
QuestionDoes the order of records change how many get an ID, and which? More in the README.
RandomisedEach record, to one of 4 lists (Section 3).
ComparedEach participant with themself: their count on each list against their count on the control (Section 7).
Lists differ inThe order: how records are sorted and cut into batches, and so, under a cap on batches, which records are served (Section 4).
ControlNewest first. Identify shows records newest first by default (iNaturalist source [3]), so identifiers who do not use this page likely meet the newest records first, which is the control's order.
Effect measuredMeeting records in one list's batches in place of newest-first batches, served with random starts (Section 5).
Primary outcomePer participant, IDs at species level or below on a tested list minus the same on newest first, each list on its own count, fixed in the analysis code: the weighted count for data-poor places first; the plain count for look-alike photos together and unfamiliar photos first (Section 6).
TestRecord-level re-randomisation test: one-sided for the confirmatory lists, Holm-adjusted over them, and two-sided, not adjusted, for the exploratory one (Section 7).
DatesRecords added up to 2026-09-11. The build was given 2026-11-01 as the blitz start. The draft protocol proposes four weeks in November 2026 and leaves the dates open.
Analysis planA draft for the BC team. It is not registered.
Records pulled10,000 BC records that need an ID,with a photoRandom spliteach record to one of 4 listsSorted and cuteach list sorts its records, pertaxon group, and cuts batches of upto 120Not servedNone. No list hasmore than 20batches in a taxongroup, the cap.Served10,000 records in 112 batches, 2,451to 2,531 records per listRotationeach browser takes the lists in itsown random turn orderParticipantsidentifiers in theblitz'siNaturalistproject or on itssign-up formRead-back and comparisoneach participant's count on eachlist against the control
Figure 1. Flow of records through this build, drawn in the style of a CONSORT flow diagram [4]. The counts are this build's. The lists are not named.

Code: manifest.py, assign.py, batches.py, page_rotation.py, analysis.py.

2. Records

#

The pull takes the BC records in iNaturalist that need an ID and have a photo, one taxon group at a time. This build holds 10,000 records and serves 10,000 of them in 112 batches.

This page is one build, of records added up to 2026-09-11, and it does not change by itself. In the blitz, a daily job adds new records, drops served records that no longer need an ID, and builds again each day (the README). The daily script defaults to all four lists with rotation. The image-based lists require embeddings prepared separately; the daily build checks the supplied files before using them.

This build is a preview sample. It is not the blitz build and not a random sample of BC records.

More detail: what the pull asks for, and what the build record keeps

The pull asks the iNaturalist API, one taxon group at a time, for records that are:

  • in British Columbia (iNaturalist place 7085)
  • need an ID (quality grade Needs ID)
  • have a photo
  • were observed on or after the pull's d1 date. The daily job uses 2025-01-01.
  • were added to iNaturalist up to the freeze date, 2026-09-11

It asks for the highest record numbers first, which are the newest records, 200 per page, and it can stop after a set number of pages. It then drops a record with no coordinates or no photo, and a record number that comes twice. The build record also has a field named d1, but it holds another date: the blitz start given to the build, 2026-11-01. The build record does not keep the pull's d1 or page cap.

Every taxon group has exactly 1,000 served records, a whole number of pages of 200, so its pull most likely stopped after a page cap. So it holds the newest records, by record number, of each taxon group added up to the freeze date.

A taxon group is one of iNaturalist's iconic taxa, for example Birds (Aves). Each taxon group gets its own batches, and you pick one on this page.

Code: inat.py, pool_state.py, daily.sh.

3. Random split

#

Each record goes to one of 4 lists at random, so on average every list holds the same mix of taxon groups and observers. The lists differ in their order and so, under a cap on batches, in which records are served. This build is keyed: a keyed hash of the record number (HMAC-SHA256 with a private key) picks the list, and a record stays on its list in every daily build.

Table 2. Served records per taxon group in this build. The lists are not named, so the table gives only the smallest and the largest list.
Taxon groupRecordsSmallest listLargest listDifference
Amphibians (Amphibia)1,00024025717
Birds (Aves)1,00023926425
Fish (Actinopterygii)1,00022726841
Fungi and lichens (Fungi)1,00024225816
Insects (Insecta)1,00023925718
Mammals (Mammalia)1,00023526126
Molluscs (Mollusca)1,00023926526
Plants (Plantae)1,00023626428
Reptiles (Reptilia)1,00024525914
Spiders and allies (Arachnida)1,00022726841
More detail: how the split is drawn, and how much list sizes differ by chance

The list is the first 8 bytes of the hash, read as a number, modulo 4. It depends only on the record number and the key, not on the rest of the pool, so it does not change as the pool grows and shrinks. The key is kept private and, by the draft protocol, opened only after the read-back.

chance, 2.5 to 97.5%this buildAmphibiansBirdsFishFungi and lichensInsectsMammalsMolluscsPlantsReptilesSpiders and allies21%23%25%27%29%
Figure 2. The smallest and the largest list in each taxon group, as a share of that group's served records. The line joins the two. The upright line marks the even share, 25 percent. The band comes from 4,000 simulated fair splits of the same number of records over the same number of lists: it runs from the 2.5th percentile of the smallest list to the 97.5th percentile of the largest, so a line that reaches past it is wider than chance usually gives. See the threats section.

The keyed split draws each record's list on its own. It does not deal a fixed number of records to each list, so list sizes in a taxon group differ by chance. For a group of 1,000 records over 4 lists, one list's size has a standard deviation of about 14 records. In this build the largest difference between the smallest and the largest list is 41 records in Fish, and the smallest is 14 records in Reptiles. Over all 10 taxon groups, 0 of the 40 lists fall outside their group's band in Figure 2. A fair split puts 0.4 lists outside on average, over the same 4,000 simulated splits. See the row on chance differences between lists in the threats section.

Code: assign.py.

4. The orders

#

This build has 4 lists, each with its own order:

Newest first. Records uploaded most recently. The control for comparison.
oldernewertime the record was added123456789101112Served order, cut into batches of up to 44batch 1712510batch 229116batch 3381
Figure 3. Newest first, on 12 made-up records, placed by the time they were added. The newest go first.
Why
The control. Identify shows records newest first by default (Section 1), so this list is close to what identifiers meet today.
Rule
Newest first sorts by the time the record was added to iNaturalist, newest first.
Parameters
The time iNaturalist gives as the record's creation time. Ties go to the higher record number.
Data-poor places first. Records from places with few observations or little recent recording.
12345678910over 20 km froma cell centre:no score1112map score:01Served order, cut into batches of up to 49batch 134710batch 22581batch 361112
Figure 4. Data-poor places first, on 12 made-up records in a made-up grid of map cells. Stronger shading means a higher score. Records 11 and 12 are more than 20 km from any cell centre, so they have no score and go last.
Why
Aims to fill gaps in where species are known to occur, the Wallacean shortfall [5], and in how recent the records are. Blitz the Gap calls these spatial and temporal gaps [1]. Its predicted direction is in Section 6.
Rule
Data-poor places first sorts by the where-to-blitz map score of the nearest map cell for the record's taxon group, highest first. The score is the map's "Species discovery" setting: its discovery measure plus 0.6 times its record-age measure, cut to 0 to 1. This code reads both measures from the where-to-blitz map files as they are. By the where-to-blitz methods note at the pinned commit, the discovery measure is higher where a cell has fewer iNaturalist records per km², and highest where it has none. The record-age measure is higher where a cell has many Research Grade records from all years but few from the last 5 years. Where where-to-blitz has no such value for a cell, it uses a stand-in from record density. A record more than 20 km from a cell centre has no score and goes last. A taxon group without its own map uses the map for all groups.
Parameters
Map: where-to-blitz, one map per taxon group, with cells of 25 km as where-to-blitz states. Score: discovery plus 0.6 times record age, cut to 0 to 1. Nearest cell centre within 20 km, else no score. Ties go to the newest record, then to the higher record number.
Look-alike photos together. Groups similar photos to test whether fewer changes of context help identifiers.
group 2group 1group 4group 31234567891011121314Served order, cut into batches of up to 45batch 167811batch 29101312batch 314341batch 42
Figure 5. Look-alike photos together, a schematic on 14 made-up photos, placed so that photos that look alike are close. Ringed photos are the start photos, from a farthest-first helper written for this figure. The build picks them with the labelfirst library. Each start grows a group of up to 4 with the build's own code, and the group number is its rank by mean map score. The batches are cut from that order, so a batch can end one group and start the next.
Why
Aims to reduce changes between kinds of photo. This trial compares ID counts, not speed.
Rule
Look-alike photos together uses an image model (BioCLIP 2.5) that turns each photo into numbers, so photos that look alike sit close together. It picks start photos spread across all the photos and grows a group of one batch size around each, adding the free photo closest to the group centre. The last group takes the photos left, so it can be smaller. Groups with the highest mean map score come first. Batches are cut from this order, so a batch can end one group and start the next.
Parameters
Image model: BioCLIP 2.5 Huge (ViT-H/14, checkpoint imageomics/bioclip-2.5-vith14), a larger successor of BioCLIP 2 [6], described in its model card [7]. The model card says it was trained on an updated version of TreeOfLife-200M, starting from a CLIP model pre-trained on LAION-2B. The model computes numbers from the record's first photo, at iNaturalist's medium size. Number of start photos: the records in the list and taxon group with image-model numbers for their first photo, divided by the batch size, rounded up. Start photos are picked farthest-first in cosine distance, the first at random [8, 9]. A start photo already taken by an earlier group is replaced by the closest free photo. Each start grows to one batch size, and the last group takes the photos left, so it can be smaller. Groups with no scored record go last. Inside a group, photos closest to the group centre come first. Records with no such numbers go last, newest first.
Unfamiliar photos first. Photos least like the Research Grade reference sample.
12345678Served order, cut into batches of up to 42batch 14865batch 2137
Figure 6. Unfamiliar photos first, on 8 made-up photos (numbered) and 15 made-up Research Grade photos (grey), placed so that photos that look alike are close. Each line joins a photo to its closest verified photo. The longest lines go first.
Why
Aims to draw attention to photos unlike the reference sample. This trial compares ID counts. An unfamiliar photo can show an ordinary species from a different angle.
Rule
Unfamiliar photos first compares each photo, with the same image model, with every Research Grade photo of its taxon group in the reference pull. Records whose closest reference photo is least alike come first.
Parameters
Image model: the same image model as above. Distance: 1 minus the highest cosine likeness to any Research Grade photo of the same taxon group. Largest distance first. Ties go to the newest record, then to the higher record number. Records with no numbers from the image model, or in a taxon group with no reference photos, go last, newest first. Reference photos: a second run of the pull code that asks for Research Grade records in place of Needs ID: BC (place 7085), with a photo, one taxon group at a time, newest first, with the dates and page cap given when it is run. The build keeps only a checksum of the file of numbers computed from those photos, not the pull's dates or page cap, so this page cannot say how many reference photos there are. The daily job does not run this pull.
More detail: batch sizes and examples

Each list sorts its records one taxon group at a time, then cuts the sorted records into batches of up to 120 records, in that order. This build has a cap of 20 batches per list and taxon group. No list reaches it, so every record is served. The figures run the order code on made-up records and cut the result into batches of up to 4.

Code: arms.py, cells.py, embed.py, batches.py.

5. Serving batches

#

Each press of Next batch takes the next of 4 lists, in a turn order your browser draws at random once, so within one build your batches split evenly over the lists while every list has batches.

So a person who opens 4 or more batches in a taxon group within one build gets every list in turn, while every list has batches. This matters because a few identifiers make most of the IDs [1]: if each person kept one list, the list that drew the busiest identifier would win whatever its order. Inside each list and taxon group your browser starts at a random batch.

The page shows no list name or letter on a batch, only its number. Your IDs carry your name and count toward Research Grade, and Research Grade records can go on to GBIF.

Turn order: each press takes the next listIdentifier 12314231Identifier 21432Start batch: one list and taxon group, 3 batchesIdentifier 1batch 2batch 3batch 1Identifier 2batch 1batch 2batch 3Each starts at a random batch, then wraps around.
Figure 7. Serving, with made-up identifiers. Turn order: each press of Next batch takes the next list in the browser's turn order, here with 4 lists. The two browsers drew different turn orders. Start batch: inside one list and taxon group, each browser starts at a random batch and wraps around. In this build a list has 2 or 3 batches in a taxon group, most often 3.

What a list changes, as you see it

A list decides which records share a batch and the order of its batches after your random start. When the build serves only the first batches of each list, it also decides which records are served (Section 4 says whether this build does). It does not decide which batch you get first, or the order inside a batch. So the test measures the effect of a list's batches, each met from a random start, against newest-first batches met the same way.

More detail: batch links, batch sizes and edge cases

Each batch is one iNaturalist Identify link that lists its record numbers and the BC place, and nothing else. Identify hides records that no longer need an ID (checked in a browser on 2026-09-11), so a batch can show fewer records than it holds. The draft protocol says Identify also hides records you already reviewed. This was not checked.

The turn order is the same for every taxon group, and each group keeps its own place in it. Inside each list and group, your browser goes on from its random start batch and wraps around to the first batch. The design borrows the within-person comparison of interleaved search tests [10]. The README draws the turn order for two made-up identifiers.

Table 3. Batches per list in each taxon group, and records per batch, in this build. The lists are not named, so the table gives the range over lists.
Taxon groupBatches per listRecords per batch
Amphibians (Amphibia)2 to 38 to 120
Birds (Aves)2 to 35 to 120
Fish (Actinopterygii)2 to 35 to 120
Fungi and lichens (Fungi)32 to 120
Insects (Insecta)2 to 312 to 120
Mammals (Mammalia)2 to 33 to 120
Molluscs (Mollusca)2 to 36 to 120
Plants (Plantae)2 to 36 to 120
Reptiles (Reptilia)35 to 120
Spiders and allies (Arachnida)2 to 312 to 120

Across this build a batch holds 2 to 120 records. A batch is the next 120 records of the sorted list, and the last batch of a list in a taxon group takes the records left, so it is usually smaller. How many records you see in a batch also depends on how many of them still need an ID when you open it.

Edge cases

  • A list with no batches left in a taxon group: the press goes to the next list in the turn order.
  • No list with batches left: the page says you opened every batch in the group.
  • A second device or browser draws its own turn order and start batches. Each one is even on its own.
  • A new daily build: your place in each taxon group resets and your start batches are drawn again. Your turn order stays.
  • Start over puts one group's batches back. The turn order and start batches stay.
  • Your place is saved in this browser only.

The batch link carries no order, so Identify sets the order inside a batch, and you can skip. A person who opens all of a list's batches in one group meets all its records that still need an ID.

Code: page_rotation.py, batches.py.

6. Outcomes

#

For each participant and list, the analysis counts the served records the participant gave an ID at species level or below (species, subspecies, variety, form, hybrid or infrahybrid), made in the window. Table 4 gives the outcomes and Table 5 what the code computes for each list.

More detail: who counts as a participant, and the window

Who counts. Participants are the identifiers in the blitz's iNaturalist project or on its sign-up form. The participants code reads one of the two, the project or the sign-up list, and writes one file of user ids. The analysis counts only participants when it is given that file. The draft protocol says to give it that file.

The window. From a start date given to the analysis up to a cut-off date given to the analysis. The code has no fixed dates. The draft protocol says IDs made during the blitz count, read back within a week of the end. Each ID keeps its own time stamp, so IDs made after the end do not count. A withdrawn ID still counts. An ID or a record deleted before the read-back is lost. The read-back is the step that asks iNaturalist, after the blitz, for every served record and its IDs.

Table 4. Outcomes. Definitions are from the code. Roles are from the draft protocol and are fixed in the analysis code. Timing is from the draft protocol.
OutcomeWhat is countedRoleRead back
Weighted countPer participant and list: served records the participant gave an ID at species level or below, each counting its map score, from 0 to 1, in place of 1. A record with no score counts 0.Primary for data-poor places first. Secondary for look-alike photos together and unfamiliar photos first, not adjusted.Within a week of the end
Plain countThe same records, each counting 1. A record counts once per person.Primary for look-alike photos together. Exploratory for unfamiliar photos first, not adjusted. Secondary for data-poor places first, not adjusted. Nothing in the code measures time.Within a week of the end
Sign testHow many participants did better on a list than on the control.Secondary, not adjustedWithin a week of the end
Species shareCode: share of served records whose community ID is at species, or that are Research Grade. The draft protocol says only: community taxon reached species.Secondary30 days after the end
Research Grade in a new cellSpecies that reached Research Grade in a grid cell with no Research Grade record of that species before the blitz. In the draft protocol, not yet in the code.Not tested. Unfamiliar photos first is judged on the plain countNot in the code
Weighted Research GradeSum of map scores over served records that reached Research Grade.Secondary30 days after the end
PlaceboThe same test on IDs made in a window before the blitz start. It should find nothing. Section 7 says what a quiet window shows.CheckNot stated
ExposureServed records each participant marked reviewed, per list. They should split about evenly. Reviews have no time stamp.CheckNot stated
Outsider sharePer list, the share of served records a non-participant identified first, against newest first's (Section 7).CheckNot stated
Table 5. What the analysis code computes for each list. It computes both counts for every list and holds each tested list to its own primary count.
ListWhat the code computes for it
Newest firstNothing of its own. It is the control that every other list is compared with. The same read-back shares are computed for it.
Data-poor places firstPrimary: the weighted count, against newest first, one-sided for this list ahead. Secondary: the plain count, not adjusted. The draft protocol predicts that this list wins on the weighted count and likely loses on the plain count.
Look-alike photos togetherPrimary: the plain count, against newest first, one-sided for this list ahead. Secondary: the weighted count, not adjusted. The draft protocol's hypothesis for this list is that fewer switches between kinds of photo make identifying faster. It does not state a direction for the count in words. The count is what is tested, not speed.
Unfamiliar photos firstExploratory: the plain count, against newest first, two-sided, not adjusted. Secondary: the weighted count, not adjusted. A species reaching Research Grade in a grid cell with no Research Grade record of it before is not in the code. This list's predicted direction on the count is not stated in the draft protocol.

Where the draft protocol states no direction for a list, this page does not supply one. The plan is in the draft protocol.

Code: readback.py, participants.py, analysis.py.

7. Statistical analysis

#

For each participant and each list other than the control, take their count on the list minus their count on the control. A fast identifier adds IDs to every list, not only to one. People with no difference drop out. The statistic is the sum of the differences over the people left. If the list makes no difference, each person's difference is symmetric about zero, so the sign-flip test, reported next to the primary p as a secondary check, flips the sign of each difference and adds again, many times [11]. p is the share of sign patterns whose sum is at least as far from zero as the real sum. With 12 or fewer people left, p is exact, from every sign pattern. With more, p comes from 10,000 random sign patterns. This check is two-sided, at 0.05.

More detail: the drawn p, and what the test assumes

The assumption. With no effect, each person's difference is symmetric about zero: +3 is as likely as -3. The sign test needs less, only that a difference is as likely positive as negative.

The drawn p. With more than 12 people left, p = (1 + count) / (10,000 + 1), where count is the number of drawn patterns at least as far out. The 1 counts the real pattern, so p is never 0 [12].

The primary test, on the records

The sign-flip test treats the person as the unit, but the design draws the split record by record. By chance the lists hold a different number of records and a different mix, which can make every person lean the same way, and flipping signs inside a person cannot separate that from the order's effect. In the power simulation this made false findings more common than the test's level (Section 8). The primary test, fixed before the blitz, redraws the split and works out the same summed difference. It replaces the sign-flip test, which stays on each list's primary count as a secondary check.

More detail: how the record-level test redraws the split

The record-level test holds each record's IDs fixed, redraws which list each record would have gone to by the same rule the build used, and works out the same summed difference. In this keyed build each record is drawn on its own and evenly over the lists, as the assignment code does. For a confirmatory comparison, one-sided, p is the share of redrawn splits whose summed difference (the list minus newest first) is at least as high as the real one, counting the real split. For every other comparison, two-sided, it is the share at least as far from zero. It assumes that every record in a list is served (Section 4 says whether this build serves every record). By default the analysis command reports this p, with the keyed redraw, as each comparison's p and Holm-adjusts the confirmatory ones. Run on one count for every list, it reports it as p_record, without adjustment.

More than one list. Each list other than the control is compared with the control on its own primary count (Table 5), so this build gives 3 comparisons. Only data-poor places first and look-alike photos together are confirmatory: tested one-sided, for the list coming out ahead of newest first, with those 2 p values adjusted by Holm's method [13]. The comparison for unfamiliar photos first is exploratory: two-sided, not adjusted. Sign test. An exact binomial test on how many people had a positive difference, zeros dropped. It is reported without adjustment.

More detail: Holm's method, and why the sign test sits next to the sum

Holm's method keeps the chance of any false finding at or below the test level over those comparisons. The smallest p is multiplied by the number of tests, the next by one less, and so on, and no adjusted p is smaller than the one before. By default the analysis command runs Holm over the confirmatory p values only, one per confirmatory list. An exploratory list's pinned count is reported marked exploratory, and each list's other count marked secondary, both without adjustment. Each window gets its own Holm step, and the code does not adjust across windows.

The sum adds up every person's difference, so people who make many IDs count for more. The sign test gives each person one vote, so the two together show whether a lift comes from a few people or from most.

Placebo. The same test on IDs from a start given to the analysis, for example the freeze date, up to the blitz start. A quiet placebo window does not prove that a finding in the blitz window is real. It only fails to show one kind of difference, one that is there without the page.

Other identifiers. For each list, the read-back command, when it is given the participants, a start and a cut-off (--users, --start, --cutoff), gives the share of served records that someone other than a participant identified, at any rank, in the window, before any participant did, and that share minus newest first's. The observer's IDs on their own record are left out on both sides: a record added during the blitz gets its observer's ID at upload, which would otherwise count as found first by someone else. The draft protocol expects newest first to lose more records this way, which favours the tested lists. The read-back keeps the time of each ID but not the time a record reached Research Grade, so this check counts IDs only. It is a check, not an outcome, and has no test.

Code: analysis.py, readback.py.

8. Sample size

#

Power is the chance that the test finds a difference that is really there. The power code simulates participants working the lists in turn and runs two comparisons: data-poor places first against newest first on the weighted count, and one generic comparison on the plain count. The 4 lists set only how each participant's effort is split and the level, 0.05/3, Holm's first step over 3 comparisons. A p that low is always a finding under Holm, so the power shown is a lower bound. A weighted run is a finding only when data-poor places first comes out ahead, a plain-count run in either direction. No participant count is fixed: the figures run from 10 to 50.

0.00.20.40.60.81.010152025304050participantspowerfactor 0.6factor 0.8factor 1.0
Figure 8. Data-poor places first on the weighted count, against newest first: simulated power at three ID-rate factors, its ID rate as a share of the rate on newest first. Blitz model, 1,000 runs per point, seed 0. Dashed line: power 0.8. A simulation, not BC data.
0.00.20.40.60.81.010152025304050participantspowerlift 0.1lift 0.2lift 0.3
Figure 9. Look-alike photos together and unfamiliar photos first, each against newest first: simulated power for three lifts. A lift of 0.2 means 20 percent more IDs per record worked. 1,000 runs per point, seed 0. Dashed line: power 0.8.

False findings. With no true difference (the preview at factor 1.0, 14,000 runs over both seeds and all participant counts), the test found one in 0.025 of runs against its level of 0.017, about 1.5 times as often, well beyond the simulation error of about 0.001. The likely reason is that the split is drawn by record: chance differences between lists make people lean the same way, which the sign-flip test cannot see. That is why the record-level p is the primary test.

Planning values, with no source. The ID-rate factors (0.6 and 0.8 and 1.0), the lifts (0.1 and 0.2 and 0.3), effort (lognormal, median 100 records, sigma 1.5) and skill, the chance of identifying a reviewed record (Beta(2, 3)), are assumed. Only the preview's map scores and the group sizes come from data.

The model assumes one build, one taxon group per participant, and even effort across lists. Its map scores come from the newest preview records, not a random sample. A binding batch cap concentrates high map scores on the data-poor list; without that cap, the lists serve the same scores on average. Simulation code and settings.

Code: power_weighted.py, power.py.

9. Threats to validity

#
Table 6. Threats to validity.
ThreatWhat could happenWhat the design does
Other identifiersPeople who do not use this page likely meet the newest records first (Section 1). Under a cap they can remove more of the control's served records, which favours the tested lists.Checked, not corrected (Section 7). Their IDs do not count in the outcomes (Section 6). The placebo window is a partial check.
Busy identifiersA few people make most IDs.Each person's batches rotate over the lists (Section 5).
Shared poolEveryone works on the same records, and an ID by one person can take a record out of a batch for others, faster on some lists.This is part of what a list does, not noise that averages out.
ID accuracyThe count is of species-level IDs, not of correct IDs.The share of served records at species level or Research Grade after 30 days is a partial check.
Knowing the listPeople may work differently on a list they think is tested.Batches carry no list name (Section 5), and each person is compared with themself.
Participants' IDs off the pageIn normal Identify participants likely meet the newest records first too, and under a cap those are mostly newest first's served records. A participant's ID on a served record counts however it was made.Not corrected. This favours the control, so it works against the tested lists.
A list runs outWhen a list has no batches left, a person's split is no longer even (Section 5).Table 3, in the batch fold of Section 5, gives this build's batches per list, and the exposure check shows how each participant's reviews split.
Change over timeRates change over time, and IDs per person may change with them.All lists run in the same weeks for the same people, so a change in how much a person identifies touches all lists alike. New records may not: daily builds add them to every list, newest first puts them in its first batches, and each other list places them by its own order.
Daily buildsA new build could move records between lists.This build is keyed, so a record stays on its list (Section 3).
Order inside a batchThe list does not set the order inside a batch (Section 5).The test is about which records meet together, not the order they are worked in (Section 5).
More than one listEach extra list is one more chance of a false finding.Holm's method adjusts the p values (Section 7).
Too few participantsThe test may miss a real lift.Section 8 gives the simulated power.
Chance differences between listsBy chance the lists hold different numbers of records and a different mix.The secondary test in Section 7 checks for this. The primary test does not correct for it.
Who takes partParticipants joined the blitz project or signed up. They are not a random sample of iNaturalist identifiers.Each person is compared with themself, so who takes part does not bias the difference. It does decide who the result is about.
Where results holdA result holds for records in British Columbia, for the dates of this build, and for the lists and settings of this build.Another region, another season or another set of orders has to run the test to know.
No map scoreRecords more than 20 km from a cell centre have no score.The weighted count gives them 0 (Section 6).

Code: analysis.py, assign.py, page_rotation.py.

10. Reproducibility

#

Each build writes a public build record. This build is keyed: the private key picks each record's list, so without the key no one can tell which list is which. The daily job also uses keyed builds.

In the blitz, the daily job keeps each day's pool and build record, reruns the build from them, and stops unless the rerun serves exactly the batches it logged.

More detail: what the rerun checks, and the build record

The rerun refuses a pool whose checksum differs, a key whose fingerprint differs, or a where-to-blitz grid whose hash differs. Image-based builds also require the original candidate and reference embeddings, matched by their recorded file hashes. Older records without these fingerprints need a new build before this check can verify them. The commands are in the README.

Table 7. What identifies this build. None of it is secret.
FieldValue
Freeze date2026-09-11
Blitz start given to the build2026-11-01
Built at2026-10-20T01:49:40+00:00
Designrotation
Assignmentkeyed
Records in the pool10,000
Records served10,000
Batch size120
Batches per list and group, at most20
Image modelbioclip25
where-to-blitz commitwhere-to-blitz@3bdcc68f8d9d17aa5c6060dcbcd965a597dfadc7
where-to-blitz grid hash2985d6eb69d5dfa14f8b1f3e75373974b6ab63e026a4f2bae4a1a58eb29afd3c
labelfirst commit791662c0f0d04698f988640aa58ece1f5cb6be59, the library that picks start photos
Code commit673b1cf8bd0963a8310a6442a7bdbdf4f7d12262, the commit of this repository that made the build
Key fingerprint7f44489e5d38, the first 12 hex characters of the SHA-256 hash of the text "what-to-id fingerprint" followed by the key. The same key gives the same fingerprint, so it shows whether two builds used the same key. It does not reveal the key.

Code links on this page show the latest code on the main branch, which can differ from the code that made this build. The code commit row above names the commit that made it.

Code: manifest.py, replay.py, daily.sh.

11. References

#
  1. Hébert K, Earley NG, Reynolds JD, et al. (2026). Blitz the Gap: a nation-wide effort to guide citizen science toward the needs of biodiversity science. EcoEvoRxiv preprint, version 5. https://doi.org/10.32942/X2T09G
  2. Mesaglio T, Shepherd KA, Wege JA, Barrett RL, Sauquet H, Cornwell WK (2025). Expert identification blitz: a rapid high value approach for assessing and improving iNaturalist identification accuracy and data precision and confidence. Plants, People, Planet 7:1469-1484. https://doi.org/10.1002/ppp3.70005
  3. iNaturalist source code, Identify's default search parameters (reviewed false, quality grade Needs ID, order by id, descending). File app/webpack/observations/identify/reducers/search_params_reducer.js, lines 10-21, commit ae0cc5d. https://github.com/inaturalist/inaturalist/blob/ae0cc5d4e8fa188151f9adc2805afdb2d0643bcd/app/webpack/observations/identify/reducers/search_params_reducer.js#L10-L21
  4. Schulz KF, Altman DG, Moher D (2010). CONSORT 2010 Statement: updated guidelines for reporting parallel group randomised trials. BMJ 340:c332. https://doi.org/10.1136/bmj.c332
  5. Hortal J, de Bello F, Diniz-Filho JAF, Lewinsohn TM, Lobo JM, Ladle RJ (2015). Seven shortfalls that beset large-scale knowledge of biodiversity. Annual Review of Ecology, Evolution, and Systematics 46:523-549. https://doi.org/10.1146/annurev-ecolsys-112414-054400
  6. Gu J, Stevens S, Campolongo EG, et al. (2025). BioCLIP 2: emergent properties from scaling hierarchical contrastive learning. NeurIPS 2025. https://arxiv.org/abs/2505.23883
  7. Imageomics (2025). BioCLIP 2.5 Huge model card: a ViT-H/14 model trained on an updated TreeOfLife-200M, starting from CLIP ViT-H/14 pre-trained on LAION-2B. Hugging Face model imageomics/bioclip-2.5-vith14. https://huggingface.co/imageomics/bioclip-2.5-vith14
  8. Gonzalez TF (1985). Clustering to minimize the maximum intercluster distance. Theoretical Computer Science 38:293-306. https://doi.org/10.1016/0304-3975(85)90224-5
  9. Sener O, Savarese S (2018). Active learning for convolutional neural networks: a core-set approach. ICLR 2018. https://arxiv.org/abs/1708.00489
  10. Chapelle O, Joachims T, Radlinski F, Yue Y (2012). Large-scale validation and analysis of interleaved search evaluation. ACM Transactions on Information Systems 30(1):6. https://doi.org/10.1145/2094072.2094078
  11. Good PI (2005). Permutation, Parametric, and Bootstrap Tests of Hypotheses, 3rd ed. Springer. https://doi.org/10.1007/b138696
  12. Phipson B, Smyth GK (2010). Permutation p-values should never be zero: calculating exact p-values when permutations are randomly drawn. Statistical Applications in Genetics and Molecular Biology 9(1):39. https://doi.org/10.2202/1544-6115.1585
  13. Holm S (1979). A simple sequentially rejective multiple test procedure. Scandinavian Journal of Statistics 6(2):65-70. https://www.jstor.org/stable/4615733

All the code and the draft protocol: PollockLab/what-to-id. Code links point at the main branch.