@bountyboard/arcade-sdk 1.4.3 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -51,7 +51,7 @@ lockToHost() → init() → [getPlayer()] → gameLoadingFinished()
51
51
  ```
52
52
 
53
53
  `gameplayStart`/`gameplayStop` bracket ACTIVE play only (not menus). They drive playtime
54
- analytics and feed ranking, so missing `gameplayStop` on pause inflates numbers and will look
54
+ analytics (feed ranking counts play sessions and ratings, not these calls), so missing `gameplayStop` on pause inflates numbers and will look
55
55
  anomalous. WebXR games must call `xrSessionStart()`/`xrSessionEnd()` around immersive sessions
56
56
  or their playtime flatlines while the headset hides the page.
57
57
 
@@ -91,4 +91,5 @@ the safe/default experience the alphabetically-first name. Don't re-randomize cl
91
91
  ## Design intent (read docs/game-design-playbook.md before building a NEW game)
92
92
 
93
93
  Leaderboard shape, daily-seed modes, session length, and ad-placement etiquette are design
94
- decisions, not integration details. The playbook covers what actually performs on Bounty Board.
94
+ decisions, not integration details. The playbook separates platform facts (enforced by the SDK and
95
+ host) from design guidance backed by cited public sources.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @bountyboard/arcade-sdk
2
2
 
3
+ ## 1.4.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 7a75783: Restyle the arcade player frame's controls on the Design Kit, and keep the CSS pseudo-fullscreen overlay above the site sidebar.
8
+
9
+ ## 1.4.4
10
+
11
+ ### Patch Changes
12
+
13
+ - Host the official Spotify embedded player beside build-backed arcade games, and let embedded-player popups escape the game iframe's sandbox.
14
+
3
15
  ## 1.4.3
4
16
 
5
17
  ### Patch Changes
@@ -1,75 +1,190 @@
1
1
  # Bounty Board game design playbook (the "design brain")
2
2
 
3
- What actually performs on the Bounty Board arcade, distilled from operating it. Read this
4
- BEFORE designing a new game or porting one in. The SDK wiring is the easy part; these choices
5
- decide whether the game earns plays, retention, and revenue.
3
+ Read this BEFORE designing a new game or porting one in. The SDK wiring is the easy part; the
4
+ choices below shape how a run feels, how the leaderboards behave, and how ads land.
6
5
 
7
- > Draft co-owned with our partner studios. Challenge anything here with data.
6
+ How to read it. Two kinds of statement live here, and each one says which it is:
7
+
8
+ - **Platform fact.** Behavior the SDK or the Bounty Board host enforces in code. These are
9
+ checkable in this package and in the host, and they change only with a release.
10
+ - **Guidance.** Design advice. Where public research or another platform's published developer
11
+ docs back it, the source is cited as [n] (see Sources at the end). Guidance without a citation
12
+ is a design judgment, not a measured result. Nothing in this file is a claim about how games
13
+ perform on Bounty Board; we have not published that data.
8
14
 
9
15
  ## Session shape
10
16
 
11
- - **Target a 60 to 180 second core loop.** Arcade traffic arrives mid browse; games that deliver a
12
- complete emotional arc (start → tension → payoff) in under three minutes get replays, and
13
- replays drive feed ranking. Longer form games need checkpointed sessions via cloud saves.
14
- - **Time to first input under 5 seconds.** Call `gameLoadingFinished()` honestly; players who
15
- bounce on a spinner never come back. Defer heavy assets past the first playable moment.
16
- - **Instant restart.** Death → new run should be ONE input and under a second. Restart friction
17
- is the top killer of "one more run".
17
+ - **Guidance: keep a full run short.** A complete arc (start, tension, payoff) in roughly one to
18
+ three minutes suits players who arrive while browsing and keeps a restart cheap. This range is
19
+ our judgment, not a measured threshold. Longer games should checkpoint through cloud saves
20
+ (see Platform fit).
21
+ - **Guidance: get to the first input fast.** Poki's developer docs note that players tend to move
22
+ to another game when loading takes more than 10 seconds [1], and recommend an initial download
23
+ of no more than 5MB and 8MB in total [2]. CrazyGames puts attention starting to wander at 10
24
+ seconds of waiting [3]. Aim well under that: defer heavy assets past the first playable moment.
25
+ - **Platform fact: `gameLoadingFinished()` means "the player can start now".** It is an alias of
26
+ `ready()` and tells the host the game has loaded and is playable. Call it at that moment, not at
27
+ script load and not after a menu the player must click through.
28
+ - **Guidance: make restart instant.** Game over to a new run should be one input and under a
29
+ second. Super Meat Boy's postmortem names this directly: "Remove lives, reduce respawn time, keep
30
+ the levels short and keep the goal always in sight" [4].
18
31
 
19
32
  ## Score design (this is leaderboard design)
20
33
 
21
- - **Scores must be integers with a meaningful gradient.** A good score curve separates a casual
22
- run from a great one by 10 to 100x, not 2x. That's what makes a board worth climbing.
23
- - **Skill ceiling over grind ceiling.** If score scales with time played rather than skill, the
24
- board saturates and goes stale. Cap or decay pure survival scoring; reward risk.
25
- - **Design the "one point short" feeling.** Near miss visibility (show the player's best and the
26
- next board rank in-game via your own UI) measurably lifts replays.
27
- - **Daily mode**: if your game has procedural content, ship a shared seed daily run and submit
28
- it with `{ mode: 'daily' }`. Daily boards reset at midnight UTC and are the strongest
29
- retention surface on the platform. Everyone plays the SAME level, so the board is fair chat.
34
+ - **Platform fact: scores are positive integers with a per game ceiling.** The SDK truncates
35
+ fractional scores, the submit endpoint accepts only positive integers up to the 32 bit limit,
36
+ and a score above the game's cap is rejected. Unless Bounty Board sets a per game cap, that
37
+ ceiling is 10,000,000; games scored by time survived can instead get a per second allowance.
38
+ Keep a great run comfortably inside the range, and tell the reviewer what a top score looks like.
39
+ - **Guidance: let skill show up in the score.** A great run should clearly beat a lucky one. If
40
+ score grows mainly with time played rather than with skill, long sessions crowd the top of the
41
+ board. Reward risk and precision; cap or decay pure survival scoring. This is design judgment;
42
+ we found no public study that sets a target spread.
43
+ - **Guidance: show players how close they came.** In a gambling study, near misses raised the
44
+ desire to keep playing when the player had some control over the outcome [5]. Research on goal
45
+ proximity finds effort rises as people near a goal: café customers bought coffee more often as
46
+ they approached a free one [6]. Both findings come from outside score chasing games, so treat
47
+ them as a reason to try this, not a promised lift. In your own UI, show the player's best and
48
+ the gap to it.
49
+ - **Platform fact: the host already shows a near miss.** After a run that sets no personal best
50
+ but lands just below a top three spot, the game page shows how many points short the player was.
51
+ Your in game UI can add finer detail (the gap to their own best, the next rank).
52
+ - **Platform fact: the Today board resets at midnight UTC.** Every submitted score lands on both
53
+ the all time board and the Today board. A run counts toward the UTC day its play session
54
+ started, so a run that crosses midnight stays on the day it began.
55
+ - **Daily mode.** If your game has procedural content, a shared seed daily run gives every player
56
+ the same level, so the board compares like with like. Spelunky's Daily Challenge is a well known
57
+ example: one attempt per day on a shared level, with a leaderboard [7]. Platform fact: submit
58
+ those runs with `{ mode: 'daily' }` on `submitScore()` / `gameOver()` so the Today board can mark
59
+ them.
30
60
 
31
61
  ## Multiplayer design (for room based games)
32
62
 
33
- - **Latency tolerant mechanics win.** Positional games at 10 to 20Hz snapshots with client
34
- interpolation feel great for chase/tag/social deduction; twitch duels don't. Design around
35
- prediction friendly movement (momentum, grid steps) rather than instant hit actions.
36
- - **Information asymmetry is a server feature.** The room server sends each player only what
37
- they may know (hiders invisible to the seeker). Lean into designs where hidden information IS
38
- the game. It's cheat proof by construction here.
39
- - **2-minute rounds, drop in lobbies.** Rooms fill from friends sharing codes; short rounds
40
- forgive mid round joins as spectators and keep groups cycling.
41
- - **Send inputs, not outcomes.** If your design needs the client to decide who got tagged, the
42
- design is wrong. Move the rule server-side.
63
+ - **Platform fact: clients send inputs; the room authority simulates.** On a Bounty Board hosted
64
+ module or a registered external server, the server runs the rules and validates every input.
65
+ If your design needs a client to decide who got tagged, move that rule to the server. Gambetta's
66
+ guide states the same principle: the server is the only authority, and you should assume
67
+ players will try to cheat [8].
68
+ - **Guidance: prefer latency tolerant mechanics.** Other players are normally drawn slightly in
69
+ the past between snapshots (entity interpolation) [9]; Valve's Source engine defaults to 20
70
+ snapshots per second and a 100 ms interpolation delay [10]. That delay suits movement based
71
+ play (chase, tag, social deduction) and makes instant hit duels hard: when you aim at a target
72
+ drawn 100 ms in the past, precise hits need server side lag compensation [9][10]. Momentum,
73
+ grid steps, and proximity rules forgive latency better than frame perfect hits.
74
+ - **Platform fact: tick and snapshot rates are set per module.** A hosted module's simulation
75
+ runs at 1 to 60 Hz, and its snapshot rate can be lower than its tick rate. The reference hide
76
+ and seek module ticks at 12 Hz; relay rooms batch messages at 20 Hz.
77
+ - **Platform fact: hosted modules filter state per viewer.** Every snapshot goes through the
78
+ module's per viewer serializer, so a player receives only what they are allowed to know. In the
79
+ reference hide and seek module, the seeker's snapshot simply does not contain unfound hiders,
80
+ so a modified client has nothing to reveal. Riot uses the same idea against wallhacks in
81
+ VALORANT: do not send an enemy's position until it could be seen [11]. This holds for refereed
82
+ modules and external authorities you build that way. Relay rooms do not interpret game payloads,
83
+ so their outcomes are client trusted and never feed results or leaderboards.
84
+ - **Platform fact: rooms fill by share code or quick match.** `joinRoom()` can create a room with
85
+ a share code, join a friend's code, or quick match into the game's open public room. Guidance:
86
+ plan for players arriving mid round. The reference hide and seek module runs a 5 second
87
+ countdown, 10 seconds of hiding, and 90 seconds of seeking, and late joiners spectate until the
88
+ next round.
43
89
 
44
90
  ## Monetization etiquette (rewarded ads)
45
91
 
46
- - **Ads are a player's trade, never a toll.** Best performing placements: revive ("continue this
47
- run?"), doubler ("2x this run's coins"), cosmetic unlock. Never gate core progression.
48
- - **One organic placement beats three pushy ones.** Interrupting flow trains players to leave;
49
- prepare one rewarded placement at a natural fail state and offer it once.
50
- - **The Watch Ad click must show directly.** Enable it only after `prepareRewardedAd()` returns
51
- ready, then call the retained `show()` before any `await`, microtask, timer, or other work.
52
- - **Always pause audio/game in `onStart`** and grant only when the final status is `'viewed'`.
53
- A raw provider `breakStatus`, dismissal, timeout, or error never earns the reward.
92
+ - **Guidance: make the ad a trade the player chooses, never a toll.** Google's AdMob policy
93
+ requires rewarded ads to be served only after the user opts in, and says declining must not
94
+ interfere with normal use of the app [12]. CrazyGames recommends stating the reward on the button
95
+ ("Watch Ad for +50 Gems"), offering it at natural pauses, and suggests revives ("Respawn now",
96
+ "Keep your score", capped at once per session) and doubled end of level rewards [13]. A cosmetic
97
+ item also fits. Never gate core progression.
98
+ - **Guidance: one placement at a natural fail state beats several interruptions.** Prepare one
99
+ rewarded placement where the run ends and offer it once. Cap anything that repeats [13].
100
+ - **Platform fact: ads are host gated.** Rewarded ads run only when Bounty Board has enabled them
101
+ for the game (or in local test mode). Otherwise preparation returns `'unavailable'`, so the
102
+ game must be complete and fair without ads.
103
+ - **Platform fact: the Watch Ad click must show directly.** Enable it only after
104
+ `prepareRewardedAd()` returns ready, then call the retained `show()` before any `await`,
105
+ microtask, timer, or other work. Browsers grant a short lived "transient activation" after a
106
+ click, and it expires after a timeout [14]; the SDK checks for it and fails closed with
107
+ `'direct_user_action_required'` when it is gone.
108
+ - **Platform fact: always pause audio/game in `onStart`** and grant only when the final status
109
+ is `'viewed'`. A raw provider `breakStatus`, dismissal, timeout, or error never earns the reward.
54
110
 
55
111
  ## Platform fit
56
112
 
57
- - **Mobile first inputs.** Most arcade sessions are touch. One thumb controls, generous hit
58
- targets, no hover dependence, portrait friendly if possible. Keyboard is the enhancement.
59
- - **Performance budget: 60fps on a mid range phone.** Cap DPR, pool objects, avoid layout
60
- thrash. Players don't report jank, they just leave.
61
- - **Own your standalone build.** The same bundle must run off platform (the SDK no-ops). Don't
62
- fork builds; feature detect through the SDK's own fallbacks.
63
- - **Cloud saves make your game feel native.** Load on boot, save on checkpoint/game over, and
64
- greet returning players with their progress (pair with `getPlayer()` for the name). Hosted
65
- builds have no localStorage, so wire this early, not as a retrofit.
113
+ - **Guidance: design for touch first.** One thumb controls, generous hit targets, no hover
114
+ dependence, portrait friendly if possible. Keyboard is the enhancement. WCAG sets 24 by 24 CSS
115
+ pixels as the minimum target size and 44 by 44 as the enhanced level [15]. Platform fact: a game
116
+ listed without touch support shows touch device players a desktop only notice, and only games
117
+ with touch support list "mobile and tablet browser" under supported devices.
118
+ - **Guidance: budget for 60fps on a mid range phone.** A frame at 60fps has about 16 ms, and
119
+ web.dev's RAIL model advises producing each animation frame in 10 ms or less because the browser
120
+ needs the rest [16]. Render into a smaller back buffer when you need speed (cap the
121
+ devicePixelRatio you honor) [17], and pool objects to keep garbage collection pauses out of play
122
+ [18]. Avoid layout thrash.
123
+ - **Platform fact: the SDK stays inert off platform.** Off Bounty Board the fire and forget calls do
124
+ nothing, `save()` / `load()` reject fast with `'unsupported'`, and `getPlayer()` resolves null.
125
+ Ship one bundle that runs standalone and on the arcade; don't fork builds.
126
+ - **Platform fact: hosted builds have no native localStorage.** Bounty Board hosted uploads run in
127
+ an opaque origin sandbox where reading `localStorage` throws. Cloud saves (`save()` / `load()`)
128
+ work for hosted uploads with a logged in player: load on boot, save on checkpoint and game over,
129
+ and greet returning players with their progress (`getPlayer()` gives the display name, or null
130
+ for guests). Engine exports that need synchronous storage can use the SDK's cloud backed
131
+ `storage` shim instead; the script tag installs it automatically, module builds call
132
+ `storage.install()` before boot. Wire this early, not as a retrofit.
66
133
 
67
134
  ## Ship checklist (design, not code)
68
135
 
69
- - [ ] A stranger reaches "I get it" inside 10 seconds of play
70
- - [ ] A full run fits in 3 minutes; restart is one input
71
- - [ ] The score of a great run embarrasses the score of a lucky run
72
- - [ ] Daily mode if content is procedural
73
- - [ ] Rewarded placement is a trade the player initiates
74
- - [ ] Playable one thumb on a phone at 60fps
136
+ - [ ] A stranger understands the goal within the first few seconds of play
137
+ - [ ] Loading reaches a playable screen well under 10 seconds [1][3]
138
+ - [ ] A full run is short; restart is one input
139
+ - [ ] The score of a great run clearly beats the score of a lucky run, inside the score cap
140
+ - [ ] Daily mode if content is procedural, submitted with `{ mode: 'daily' }`
141
+ - [ ] Rewarded placement is a trade the player initiates, and the game is complete without it
142
+ - [ ] Playable one thumb on a phone, aiming for 60fps
75
143
  - [ ] Boots and plays with the SDK fully offline
144
+
145
+ ## Sources
146
+
147
+ Platform facts are backed by the code in this package (`src/types.ts`, `src/core.ts`,
148
+ `src/storage.ts`, `src/multiplayer.ts`), the room server (`packages/arcade-mp-server`), and the
149
+ Bounty Board host, not by the links below.
150
+
151
+ [1] Poki Developer Docs, "Requirements".
152
+ https://developers.poki.com/guide/requirements-quality
153
+ [2] Poki Developer Docs, "Choosing your web game engine".
154
+ https://developers.poki.com/guide/web-engine
155
+ [3] CrazyGames Docs, "Game loading tips".
156
+ https://docs.crazygames.com/resources/getting-to-the-first-frame/
157
+ [4] Edmund McMillen, "Postmortem: Team Meat's Super Meat Boy", Game Developer, 2011.
158
+ https://www.gamedeveloper.com/audio/postmortem-team-meat-s-i-super-meat-boy-i-
159
+ [5] Clark, Lawrence, Astley-Jones and Gray, "Gambling near-misses enhance motivation to gamble
160
+ and recruit win-related brain circuitry", Neuron 61(3), 2009. doi:10.1016/j.neuron.2008.12.031
161
+ https://pmc.ncbi.nlm.nih.gov/articles/PMC2658737/
162
+ [6] Kivetz, Urminsky and Zheng, "The Goal-Gradient Hypothesis Resurrected", Journal of Marketing
163
+ Research 43(1), 2006. doi:10.1509/jmkr.43.1.39
164
+ https://journals.sagepub.com/doi/abs/10.1509/jmkr.43.1.39
165
+ [7] Mossmouth, "The Spelunky Daily Challenge".
166
+ https://spelunkyworld.com/dailychallenge/
167
+ [8] Gabriel Gambetta, "Fast-Paced Multiplayer (Part I): Client-Server Game Architecture".
168
+ https://www.gabrielgambetta.com/client-server-game-architecture.html
169
+ [9] Gabriel Gambetta, "Fast-Paced Multiplayer (Part III): Entity Interpolation".
170
+ https://www.gabrielgambetta.com/entity-interpolation.html
171
+ [10] Valve Developer Community, "Source Multiplayer Networking".
172
+ https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
173
+ [11] Riot Games, "Demolishing Wallhacks with VALORANT's Fog of War", 2020.
174
+ https://www.riotgames.com/en/news/demolishing-wallhacks-valorants-fog-war
175
+ [12] Google AdMob Help, "Policies for ad units that offer rewards".
176
+ https://support.google.com/admob/answer/7313578
177
+ [13] CrazyGames Docs, "Mastering Rewarded Ads: A Deep Dive".
178
+ https://docs.crazygames.com/resources/rewarded-ads-deep-dive/
179
+ [14] MDN Web Docs, "User activation".
180
+ https://developer.mozilla.org/en-US/docs/Web/Security/User_activation
181
+ [15] W3C, "Understanding SC 2.5.8 Target Size (Minimum)" and "Understanding SC 2.5.5 Target Size
182
+ (Enhanced)", WCAG 2.2.
183
+ https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
184
+ https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced.html
185
+ [16] web.dev, "Measure performance with the RAIL model".
186
+ https://web.dev/articles/rail
187
+ [17] MDN Web Docs, "WebGL best practices".
188
+ https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices
189
+ [18] web.dev, "Static Memory JavaScript with Object Pools".
190
+ https://web.dev/speed-static-mem-pools/
package/docs/llms.txt CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Human guide: https://www.bountyboard.gg/arcade/sdk
4
4
  Package: @bountyboard/arcade-sdk (https://www.npmjs.com/package/@bountyboard/arcade-sdk)
5
- Stable npm release: 1.4.3
5
+ Stable npm release: 1.4.5
6
6
  Wire protocol: 1
7
7
 
8
8
  This file is the complete integration contract for coding agents integrating an
9
9
  HTML5 game. The package TypeScript declarations remain the exact public type
10
- reference. npm 1.4.3, the /arcade-sdk/v1.js browser artifact, and repository
10
+ reference. npm 1.4.5, the /arcade-sdk/v1.js browser artifact, and repository
11
11
  source all expose the same surface. Rooms open on all three approved rails:
12
12
  Bounty shared service referee modules, Bounty shared service relay rooms, and
13
13
  host routed registered external authorities. code/create works on every rail;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bountyboard/arcade-sdk",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "Bounty Board Arcade SDK: leaderboards, cloud saves, rewarded ads, A/B variants, and multiplayer rooms for games on bountyboard.gg",
5
5
  "keywords": [
6
6
  "bountyboard",