Dated source reference. Current release status: handoff state. Full original relative paths are in the developer archive.
Visiting crowd artwork
Private API 0.2.0.dev58 and TypeScript SDK 0.1.0-dev.3 add a separate away-fan percentage. The private dev58 build has original/college production-match checks, separately reviewed native images and bounded zero-start/late-enable checks; exact scope is recorded in work78. The accepted dev57 attendance bundle remains unchanged. The candidate uses the selected teams' actual crowd clothing and palette resources through runtime hooks. No arena, model, stadium or floor files are edited or generated.
Choose both controls in an ordinary match launch request:
{
"version": 1,
"rules": {"profile": "original"},
"crowd": {"fill": 65, "away_percent": 25}
}
fill means percentage fullness. away_percent means the requested percentage assigned away-team clothing and colors. Both accept finite numbers from 0 to 100, including fractional percentages. The API converts each to the hook's internal fraction. Either field can be used alone. Omitting away_percent preserves native appearance; explicit zero selects home appearance throughout, and 100 selects away appearance throughout. Fullness and appearance remain independent. College rules use the same fields.
The runtime assigns whole crowd rows so close spectators and their distant representations keep the same affiliation. Assignments form a stable nested set as the percentage changes and do not consume game randomness. Actual assignment can differ slightly from the request because rows have different sizes. This initial version distributes visitors globally; placement behind a bench and named section selection are not implemented.
Change away share during play
Use the ordinary authenticated API and exclusive control lease. The helper reads fresh state and prepares the guarded command; the application retains that body before submitting it.
lease = client.request('/v1/control/lease', {
'client_id': 'my-dynasty', 'ttl_seconds': 30
})
body = client.crowd_appearance_command(
25, client_id='my-dynasty', lease_token=lease['lease_token'],
command_id='match-123-away-crowd-1'
)
# Persist the complete body privately before submission.
receipt = client.request('/v1/commands', body)
view = client.crowd_appearance()
client.request('/v1/control/release', {'lease_token': lease['lease_token']})
TypeScript exposes the same operation:
const body = await client.crowdAppearanceCommand(25, {
client_id: 'my-dynasty', lease_token: lease.lease_token,
command_id: 'match-123-away-crowd-1'
});
// Persist the complete body privately before submission.
const receipt = await client.request('POST /v1/commands', {body});
const view = await client.crowdAppearance();
Pass Python None or TypeScript null to prepare crowd.appearance.reset. Reset restores native appearance and reports its unknown away share as null; zero is an explicit home-only override. crowd.appearance.set carries a percentage value; reset carries no value. The separate crowd.set and crowd.reset commands continue to control fullness.
The runnable Python example saves a private command file before submission. Reuse the exact command body and ID after a lost response. An indeterminate result requires checking the receipt and current state before another mutation. In particular, a missing render acknowledgement can leave the target applied without a confirmed draw boundary. The SDK does not retry mutations automatically.
Readback and limits
GET /v1/crowd/appearance returns health, run_id, the capability catalog, and nullable state. Require health: "ready". State declares version 1 and percent units and is also included as snapshot.crowd_appearance when the matching hook is present.
| Field | Meaning |
|---|---|
away_percent |
Requested away share, or null for native appearance |
assigned_percent |
Weighted row assignment, independent of camera visibility |
occupied_away_percent |
Share among currently eligible audience anchors, or null when none are occupied |
home, away |
Native graphics identity, resolved team filename, source SHA256 and special-team fallback flag |
startup |
Original launch request and readiness, independent of subsequent live changes |
native_calls |
Close, distant, atlas and retirement diagnostics; these establish recorded activity, not visual approval |
Managed launch requires a hook exporting OpenHoopsCrowdAppearanceVersion=1 and waits for requested appearance resources before revealing play. Live changes are run/period/scene/epoch/revision/preimage and lease guarded. Stale observations, replay, finished games and unavailable adapters are refused.
Explicit zero uses the deterministic home population; it does not promise byte-identical native random clothing. The candidate retains up to four appearance scenes until the game process exits, keeping textures and materials alive for deferred rendering. Unsupported layouts or insufficient command-buffer reserve refuse an opening request, or restore native appearance with an error after play has been revealed. Applications must inspect that error. Retirement counters are diagnostics and do not establish command-buffer completion.
Some supplied team files contain identical generic artwork or the game's special-team fallback. The hook preserves and reports that source rather than inventing distinctive shirts. This does not change crowd audio, players, bench personnel or spectators baked into static custom meshes. Replacement mip levels use a defined box filter on decoded authentic top-level artwork; equivalence to every original packed mip is not claimed. Native render acceptance is recorded separately in work78.