@indiegems/gem-web-sdk 10.1.1 → 10.3.0

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.
@@ -1,6 +1,6 @@
1
1
  # The browser surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v10.1.1, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v10.3.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
4
4
 
5
5
  `@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
6
6
 
@@ -4590,6 +4590,417 @@ only when your studio corrects a running period; clients may hold the old
4590
4590
  content for up to an hour afterwards, and the hash is how a verifier tells
4591
4591
  them apart.
4592
4592
 
4593
+ <a id="gemlookspeed"></a>
4594
+
4595
+ ## GemLookSpeed
4596
+
4597
+ ```ts
4598
+ interface GemLookSpeed
4599
+ ```
4600
+
4601
+ A look speed per axis, in radians: per CSS px for a pointer, per second at
4602
+ full tilt for a stick.
4603
+
4604
+ <a id="gemlookspeed-yaw"></a>
4605
+
4606
+ ## GemLookSpeed.yaw
4607
+
4608
+ ```ts
4609
+ readonly yaw: number
4610
+ ```
4611
+
4612
+ Across: radians of yaw.
4613
+
4614
+ <a id="gemlookspeed-pitch"></a>
4615
+
4616
+ ## GemLookSpeed.pitch
4617
+
4618
+ ```ts
4619
+ readonly pitch: number
4620
+ ```
4621
+
4622
+ Up/down: radians of pitch.
4623
+
4624
+ <a id="gemlookbase"></a>
4625
+
4626
+ ## GemLookBase
4627
+
4628
+ ```ts
4629
+ interface GemLookBase
4630
+ ```
4631
+
4632
+ The shape of `gem.controls.LOOK_BASE`: Roblox's base look speeds and the
4633
+ stick and touch shaping constants `gem.controls.look()` applies.
4634
+
4635
+ <a id="gemlookbase-touch"></a>
4636
+
4637
+ ## GemLookBase.touch
4638
+
4639
+ ```ts
4640
+ readonly touch: GemLookSpeed
4641
+ ```
4642
+
4643
+ Radians per CSS px, for touch and pen: 1° across, 0.66° up/down.
4644
+
4645
+ <a id="gemlookbase-mouse"></a>
4646
+
4647
+ ## GemLookBase.mouse
4648
+
4649
+ ```ts
4650
+ readonly mouse: GemLookSpeed
4651
+ ```
4652
+
4653
+ Radians per CSS px, for a mouse: 0.5° across, 0.385° up/down.
4654
+
4655
+ <a id="gemlookbase-stick"></a>
4656
+
4657
+ ## GemLookBase.stick
4658
+
4659
+ ```ts
4660
+ readonly stick: GemLookSpeed
4661
+ ```
4662
+
4663
+ Radians per second at full tilt, for a gamepad stick: 240° across, 184.8° up/down.
4664
+
4665
+ <a id="gemlookbase-stickdeadzone"></a>
4666
+
4667
+ ## GemLookBase.stickDeadzone
4668
+
4669
+ ```ts
4670
+ readonly stickDeadzone: number
4671
+ ```
4672
+
4673
+ A stick axis below this magnitude turns nothing: 0.1.
4674
+
4675
+ <a id="gemlookbase-stickcurve"></a>
4676
+
4677
+ ## GemLookBase.stickCurve
4678
+
4679
+ ```ts
4680
+ readonly stickCurve: number
4681
+ ```
4682
+
4683
+ The stick curve's curvature `k` in `(e^(k·d) − 1) / (e^k − 1)`: 2.
4684
+
4685
+ <a id="gemlookbase-touchpitchmin"></a>
4686
+
4687
+ ## GemLookBase.touchPitchMin
4688
+
4689
+ ```ts
4690
+ readonly touchPitchMin: number
4691
+ ```
4692
+
4693
+ The least fraction of the pitch speed a touch drag keeps straight up or down: 0.25.
4694
+
4695
+ <a id="gemlookoptions"></a>
4696
+
4697
+ ## GemLookOptions
4698
+
4699
+ ```ts
4700
+ interface GemLookOptions
4701
+ ```
4702
+
4703
+ The game's own overrides for `gem.controls.look()` — what the player chose in
4704
+ YOUR settings screen. Each one wins over the Arcade's value; leave one out to
4705
+ follow the Arcade.
4706
+
4707
+ <a id="gemlookoptions-sensitivity"></a>
4708
+
4709
+ ## GemLookOptions.sensitivity
4710
+
4711
+ ```ts
4712
+ readonly sensitivity?: number
4713
+ ```
4714
+
4715
+ The player's own look speed for touch, mouse and pen, as a multiplier
4716
+ (`1` = the base speed). Wins over `gem.prefs.recommended.lookSensitivity`.
4717
+
4718
+ <a id="gemlookoptions-gamepadsensitivity"></a>
4719
+
4720
+ ## GemLookOptions.gamepadSensitivity
4721
+
4722
+ ```ts
4723
+ readonly gamepadSensitivity?: number
4724
+ ```
4725
+
4726
+ The player's own look speed for a gamepad stick, as a multiplier. Wins
4727
+ over `gem.prefs.recommended.gamepadLookSensitivity`.
4728
+
4729
+ <a id="gemlookoptions-inverty"></a>
4730
+
4731
+ ## GemLookOptions.invertY
4732
+
4733
+ ```ts
4734
+ readonly invertY?: boolean
4735
+ ```
4736
+
4737
+ The player's own inverted up/down look. Wins over
4738
+ `gem.prefs.recommended.invertLookY`.
4739
+
4740
+ <a id="gemlookdelta"></a>
4741
+
4742
+ ## GemLookDelta
4743
+
4744
+ ```ts
4745
+ interface GemLookDelta
4746
+ ```
4747
+
4748
+ A turn to apply to your camera, in radians to ADD: `yaw` positive turns
4749
+ right (clockwise seen from above), `pitch` positive looks up.
4750
+
4751
+ <a id="gemlookdelta-yaw"></a>
4752
+
4753
+ ## GemLookDelta.yaw
4754
+
4755
+ ```ts
4756
+ readonly yaw: number
4757
+ ```
4758
+
4759
+ Radians to add to the camera's yaw; positive turns right.
4760
+
4761
+ <a id="gemlookdelta-pitch"></a>
4762
+
4763
+ ## GemLookDelta.pitch
4764
+
4765
+ ```ts
4766
+ readonly pitch: number
4767
+ ```
4768
+
4769
+ Radians to add to the camera's pitch; positive looks up.
4770
+
4771
+ <a id="gemlook"></a>
4772
+
4773
+ ## GemLook
4774
+
4775
+ ```ts
4776
+ interface GemLook
4777
+ ```
4778
+
4779
+ The look helper `gem.controls.look()` returns. Pure math: it listens to
4780
+ nothing and moves no camera — feed it the deltas your input code already
4781
+ has and add what it returns to your camera's yaw and pitch, then clamp the
4782
+ pitch as you always do.
4783
+
4784
+ <a id="gemlook-frompointer"></a>
4785
+
4786
+ ## GemLook.fromPointer
4787
+
4788
+ ```ts
4789
+ fromPointer(dx: number, dy: number, pointerType: string, pitch?: number): GemLookDelta
4790
+ ```
4791
+
4792
+ A pointer move — a touch drag, a mouse move (under pointer lock,
4793
+ `movementX` / `movementY`) or a pen — turned into a look turn.
4794
+
4795
+ | Parameter | Type | Description |
4796
+ | --- | --- | --- |
4797
+ | `dx` | `number` | The move across, in CSS px, right positive. |
4798
+ | `dy` | `number` | The move down the screen, in CSS px, DOWN positive (as
4799
+ `PointerEvent` reports it). Dragging up looks up. |
4800
+ | `pointerType` | `string` | The event's `pointerType`: `'touch'` and `'pen'` take
4801
+ the touch speeds, anything else the mouse speeds. |
4802
+ | `pitch` | `number` | The camera's current pitch in radians (0 = the horizon,
4803
+ positive = looking up), for the touch slowdown near straight up or
4804
+ down. Leave it out to skip the slowdown. |
4805
+
4806
+ <a id="gemlook-fromstick"></a>
4807
+
4808
+ ## GemLook.fromStick
4809
+
4810
+ ```ts
4811
+ fromStick(x: number, y: number, dt: number): GemLookDelta
4812
+ ```
4813
+
4814
+ A right stick turned into a look turn over one frame.
4815
+
4816
+ | Parameter | Type | Description |
4817
+ | --- | --- | --- |
4818
+ | `x` | `number` | The stick's horizontal axis as the Gamepad API reports it,
4819
+ −1..1, right positive. |
4820
+ | `y` | `number` | The stick's vertical axis as the Gamepad API reports it, −1..1,
4821
+ DOWN positive. Pushing up looks up. |
4822
+ | `dt` | `number` | The frame's duration in seconds. |
4823
+
4824
+ <a id="gemlook-set"></a>
4825
+
4826
+ ## GemLook.set
4827
+
4828
+ ```ts
4829
+ set(options: GemLookOptions): void
4830
+ ```
4831
+
4832
+ Change your game's overrides — call it when the player changes a setting
4833
+ in your settings screen. Each key you pass replaces that override; a key
4834
+ passed as `undefined` removes it, so that input follows the Arcade again;
4835
+ a key you leave out is unchanged.
4836
+
4837
+ | Parameter | Type |
4838
+ | --- | --- |
4839
+ | `options` | `GemLookOptions` |
4840
+
4841
+ **Throws.** TypeError when a sensitivity is not a finite number above 0, or
4842
+ `invertY` is not a boolean.
4843
+
4844
+ <a id="controlsapi"></a>
4845
+
4846
+ ## ControlsApi
4847
+
4848
+ ```ts
4849
+ interface ControlsApi
4850
+ ```
4851
+
4852
+ `gem.controls` — the player's camera and controls settings: tell the Arcade
4853
+ which ones your game leaves to the player, and turn look input into a camera
4854
+ turn at the player's sensitivity.
4855
+
4856
+ **Guides.** [what-a-game-must-do](../what-a-game-must-do.md)
4857
+
4858
+ <a id="controlsapi-declare"></a>
4859
+
4860
+ ## ControlsApi.declare
4861
+
4862
+ ```ts
4863
+ declare(uses: GemControlsDeclaration): boolean
4864
+ ```
4865
+
4866
+ Tell the Arcade which of the player's camera and controls settings your
4867
+ game uses, and for each one who decides it: `'player'` — you apply the
4868
+ Arcade's value from `gem.prefs.recommended` (or the player's own choice
4869
+ in your settings, which wins) — or `'game'` — you decide it yourself, and
4870
+ the Arcade's Settings shows that row as "Set by this game". Leave out a
4871
+ setting your game does not use.
4872
+
4873
+ ```js
4874
+ gem.controls.declare({
4875
+ lookSensitivity: 'player',
4876
+ gamepadLookSensitivity: 'player',
4877
+ invertLookY: 'player',
4878
+ touchMovement: 'game', // this game always uses a fixed stick
4879
+ });
4880
+ ```
4881
+
4882
+ **It is screen state, not a toggle.** Each call is the whole answer and
4883
+ replaces the last. The SDK holds it and sends it again after every
4884
+ reconnect, so call it once at boot, and again only when the answer
4885
+ changes. Any other key or value is dropped.
4886
+
4887
+ | Parameter | Type |
4888
+ | --- | --- |
4889
+ | `uses` | `GemControlsDeclaration` |
4890
+
4891
+ **Returns.** `true` when the Arcade was told now; `false` before the bridge is
4892
+ up (the declaration is held and sent when it connects), outside the
4893
+ Arcade, and under an embedder that does not show these settings. Never
4894
+ throws.
4895
+
4896
+ <a id="controlsapi-look"></a>
4897
+
4898
+ ## ControlsApi.look
4899
+
4900
+ ```ts
4901
+ look(options?: GemLookOptions): GemLook
4902
+ ```
4903
+
4904
+ Make a look helper: Roblox's camera-input math at the player's look
4905
+ sensitivity and invert. Make one and keep it.
4906
+
4907
+ ```js
4908
+ const look = gem.controls.look();
4909
+ // A mouse under pointer lock; for touch, pass the change in clientX/clientY.
4910
+ canvas.addEventListener('pointermove', (e) => {
4911
+ if (document.pointerLockElement !== canvas) return;
4912
+ const t = look.fromPointer(e.movementX, e.movementY, e.pointerType, camera.pitch);
4913
+ camera.yaw += t.yaw;
4914
+ camera.pitch = clamp(camera.pitch + t.pitch, -1.4, 1.4);
4915
+ });
4916
+ ```
4917
+
4918
+ The sensitivity and invert are read **live** on every call: the Arcade's
4919
+ `gem.prefs.recommended` values, unless `options` (or a later
4920
+ [GemLook.set](#gemlook-set)) gives your game's own, which win.
4921
+
4922
+ | Parameter | Type |
4923
+ | --- | --- |
4924
+ | `options` | `GemLookOptions` |
4925
+
4926
+ **Throws.** TypeError when an option is malformed, as [GemLook.set](#gemlook-set).
4927
+
4928
+ <a id="controlsapi-look_base"></a>
4929
+
4930
+ ## ControlsApi.LOOK_BASE
4931
+
4932
+ ```ts
4933
+ readonly LOOK_BASE: GemLookBase
4934
+ ```
4935
+
4936
+ The base speeds `look` applies at 1×, in radians. Frozen.
4937
+
4938
+ <a id="gemcontrolchoice"></a>
4939
+
4940
+ ## GemControlChoice
4941
+
4942
+ ```ts
4943
+ type GemControlChoice = "player" | "game"
4944
+ ```
4945
+
4946
+ Who decides one camera or controls setting: `'player'` — your game applies
4947
+ the Arcade's value, or the player's own in-game choice, which wins — or
4948
+ `'game'` — your game decides it itself, and the Arcade's Settings shows the
4949
+ row as "Set by this game".
4950
+
4951
+ <a id="gemcontrolsdeclaration"></a>
4952
+
4953
+ ## GemControlsDeclaration
4954
+
4955
+ ```ts
4956
+ interface GemControlsDeclaration
4957
+ ```
4958
+
4959
+ What `gem.controls.declare` takes: for each of the player's camera and
4960
+ controls settings your game uses, who decides it. Leave a setting out when
4961
+ your game does not use it at all — the Arcade then says "This game doesn't
4962
+ use this" beside that row.
4963
+
4964
+ <a id="gemcontrolsdeclaration-looksensitivity"></a>
4965
+
4966
+ ## GemControlsDeclaration.lookSensitivity
4967
+
4968
+ ```ts
4969
+ readonly lookSensitivity?: GemControlChoice
4970
+ ```
4971
+
4972
+ Look speed for touch, mouse and pen — `gem.prefs.recommended.lookSensitivity`.
4973
+
4974
+ <a id="gemcontrolsdeclaration-gamepadlooksensitivity"></a>
4975
+
4976
+ ## GemControlsDeclaration.gamepadLookSensitivity
4977
+
4978
+ ```ts
4979
+ readonly gamepadLookSensitivity?: GemControlChoice
4980
+ ```
4981
+
4982
+ Look speed for a gamepad stick — `gem.prefs.recommended.gamepadLookSensitivity`.
4983
+
4984
+ <a id="gemcontrolsdeclaration-invertlooky"></a>
4985
+
4986
+ ## GemControlsDeclaration.invertLookY
4987
+
4988
+ ```ts
4989
+ readonly invertLookY?: GemControlChoice
4990
+ ```
4991
+
4992
+ Inverted up/down look — `gem.prefs.recommended.invertLookY`.
4993
+
4994
+ <a id="gemcontrolsdeclaration-touchmovement"></a>
4995
+
4996
+ ## GemControlsDeclaration.touchMovement
4997
+
4998
+ ```ts
4999
+ readonly touchMovement?: GemControlChoice
5000
+ ```
5001
+
5002
+ The touch movement thumbstick, floating or fixed — `gem.prefs.recommended.touchMovement`.
5003
+
4593
5004
  <a id="device_settings_max_bytes"></a>
4594
5005
 
4595
5006
  ## DEVICE_SETTINGS_MAX_BYTES
@@ -5149,6 +5560,30 @@ wallet.
5149
5560
  **Returns.** whether the message left the frame — `false` before the bridge is
5150
5561
  up, for an arcade with no wallet, and past this frame's own limit.
5151
5562
 
5563
+ <a id="gemframe-declarecontrols"></a>
5564
+
5565
+ ## GemFrame.declareControls
5566
+
5567
+ ```ts
5568
+ declareControls(uses: GemControlsDeclaration): boolean
5569
+ ```
5570
+
5571
+ Tell the arcade which of the player's camera and controls settings your
5572
+ game leaves to the player and which it decides itself — the whole answer
5573
+ each time, the latest one winning.
5574
+
5575
+ Prefer `gem.controls.declare`, which holds the declaration across a
5576
+ reconnect. This one sends only while connected, and only to an arcade
5577
+ that shows these settings.
5578
+
5579
+ | Parameter | Type |
5580
+ | --- | --- |
5581
+ | `uses` | `GemControlsDeclaration` |
5582
+
5583
+ **Returns.** whether the message left the frame — `false` before the bridge is
5584
+ up, for an arcade that does not show these settings, and past this
5585
+ frame's own limit.
5586
+
5152
5587
  <a id="gemframe-setmenu"></a>
5153
5588
 
5154
5589
  ## GemFrame.setMenu
@@ -5198,6 +5633,24 @@ arcade that handles share.
5198
5633
  up, for an arcade that does not handle share, and past this frame's own
5199
5634
  limit of six a minute.
5200
5635
 
5636
+ <a id="gemframe-requestavatarpicker"></a>
5637
+
5638
+ ## GemFrame.requestAvatarPicker
5639
+
5640
+ ```ts
5641
+ requestAvatarPicker(): boolean
5642
+ ```
5643
+
5644
+ Ask the arcade to open its own avatar picker over your game. Nothing comes
5645
+ back: a changed portrait arrives as a new `gem.player`, through `onPlayer`.
5646
+
5647
+ Prefer `gem.openAvatarPicker`, which documents what a game does with the
5648
+ change.
5649
+
5650
+ **Returns.** whether the message left the frame — `false` before the bridge is
5651
+ up, for an arcade that has no avatar picker here, and past this frame's
5652
+ own limit of six a minute.
5653
+
5201
5654
  <a id="gemframe-requestvibrate"></a>
5202
5655
 
5203
5656
  ## GemFrame.requestVibrate
@@ -5332,7 +5785,10 @@ The precomposed `Name#0001`. It contains the name, so it is
5332
5785
  readonly avatarIconId?: string
5333
5786
  ```
5334
5787
 
5335
- The id of the player's portrait item, when they have one.
5788
+ The id of the player's portrait item, when they have one — what a game
5789
+ that draws Arcade avatars keys its own model on. On `gem.player` it
5790
+ changes when the player wears another portrait, and `onPlayer` runs again;
5791
+ see `gem.openAvatarPicker`.
5336
5792
 
5337
5793
  <a id="friend-avatariconurl"></a>
5338
5794
 
@@ -7137,7 +7593,10 @@ The precomposed `Name#0001`. It contains the name, so it is
7137
7593
  readonly avatarIconId?: string
7138
7594
  ```
7139
7595
 
7140
- The id of the player's portrait item, when they have one.
7596
+ The id of the player's portrait item, when they have one — what a game
7597
+ that draws Arcade avatars keys its own model on. On `gem.player` it
7598
+ changes when the player wears another portrait, and `onPlayer` runs again;
7599
+ see `gem.openAvatarPicker`.
7141
7600
 
7142
7601
  <a id="gemplayer-avatariconurl"></a>
7143
7602
 
@@ -7244,8 +7703,9 @@ readonly recommended: GemRecommendedSettings | null
7244
7703
  ```
7245
7704
 
7246
7705
  The player's Arcade-wide defaults for this device — a volume, battery
7247
- saver, a keyboard layout, the gamepad's confirm button and vibration — or
7248
- `null` when the Arcade did not send them.
7706
+ saver, a keyboard layout, the gamepad's confirm button, vibration, and the
7707
+ camera and controls settings (look sensitivity, gamepad look sensitivity,
7708
+ invert look, touch movement) — or `null` when the Arcade did not send them.
7249
7709
 
7250
7710
  **Recommendations only; the player's own choice in your game wins.** Start
7251
7711
  a setting from here while the player has made no choice of their own for
@@ -7329,6 +7789,54 @@ readonly vibration: boolean
7329
7789
 
7330
7790
  Whether the player wants vibration and controller rumble.
7331
7791
 
7792
+ <a id="gemrecommendedsettings-looksensitivity"></a>
7793
+
7794
+ ## GemRecommendedSettings.lookSensitivity
7795
+
7796
+ ```ts
7797
+ readonly lookSensitivity: number
7798
+ ```
7799
+
7800
+ The player's look speed for touch, mouse and pen, as a percentage of the
7801
+ base speeds: `100` is 1×, from `20` (0.2×) to `400` (4×). `100` when the
7802
+ Arcade did not say. `gem.controls.look()` applies it for you.
7803
+
7804
+ <a id="gemrecommendedsettings-gamepadlooksensitivity"></a>
7805
+
7806
+ ## GemRecommendedSettings.gamepadLookSensitivity
7807
+
7808
+ ```ts
7809
+ readonly gamepadLookSensitivity: number
7810
+ ```
7811
+
7812
+ The player's look speed for a gamepad stick, as a percentage: `100` is 1×,
7813
+ from `20` to `400`. `100` when the Arcade did not say.
7814
+ `gem.controls.look()` applies it for you.
7815
+
7816
+ <a id="gemrecommendedsettings-invertlooky"></a>
7817
+
7818
+ ## GemRecommendedSettings.invertLookY
7819
+
7820
+ ```ts
7821
+ readonly invertLookY: boolean
7822
+ ```
7823
+
7824
+ Whether the player wants up/down look inverted: pushing up or dragging up
7825
+ looks down. Flips pitch only, never yaw. `false` when the Arcade did not
7826
+ say. `gem.controls.look()` applies it for you.
7827
+
7828
+ <a id="gemrecommendedsettings-touchmovement"></a>
7829
+
7830
+ ## GemRecommendedSettings.touchMovement
7831
+
7832
+ ```ts
7833
+ readonly touchMovement: "floating" | "fixed"
7834
+ ```
7835
+
7836
+ On a touch screen, the movement thumbstick the player wants: `'floating'`
7837
+ appears where the thumb lands, `'fixed'` stays in one place. `'floating'`
7838
+ when the Arcade did not say. Your game draws the stick.
7839
+
7332
7840
  <a id="gemcohort"></a>
7333
7841
 
7334
7842
  ## GemCohort
@@ -8108,6 +8616,63 @@ shareButton.onclick = () => {
8108
8616
 
8109
8617
  **Guides.** [getting-started](../getting-started.md)
8110
8618
 
8619
+ <a id="gem-avatarpickeravailable"></a>
8620
+
8621
+ ## Gem.avatarPickerAvailable
8622
+
8623
+ ```ts
8624
+ readonly avatarPickerAvailable: boolean
8625
+ ```
8626
+
8627
+ Whether the arcade will open its avatar picker for [Gem.openAvatarPicker](#gem-openavatarpicker)
8628
+ here. Decide from it whether to show your own "Change avatar" button, and
8629
+ read it again in the click handler — it is read live.
8630
+
8631
+ `false` outside the arcade, before the bridge is up, after the session
8632
+ ends, and under an embedder that has no avatar picker.
8633
+
8634
+ <a id="gem-openavatarpicker"></a>
8635
+
8636
+ ## Gem.openAvatarPicker
8637
+
8638
+ ```ts
8639
+ openAvatarPicker(): boolean
8640
+ ```
8641
+
8642
+ Open the arcade's avatar picker over your game, so the player can change
8643
+ the Arcade avatar they wear without leaving it. For a game that draws
8644
+ Arcade avatars — whole characters the player owns as Arcade items — put a
8645
+ "Change avatar" button in your own UI and call this from it:
8646
+
8647
+ ```js
8648
+ changeAvatar.hidden = !gem.avatarPickerAvailable;
8649
+ changeAvatar.onclick = () => {
8650
+ if (!gem.openAvatarPicker()) changeAvatar.hidden = true;
8651
+ };
8652
+ ```
8653
+
8654
+ **The change arrives as a new player, not as an answer.** Nothing comes
8655
+ back from this call. When the player picks a different avatar, the arcade
8656
+ pauses your game while the picker is open, then sends the player again —
8657
+ your `onPlayer` callback runs and [Gem.player](#gem-player) holds the new
8658
+ [PlayerIdentity.avatarIconId](#playeridentity-avatariconid) — and resumes it. Redraw from
8659
+ `onPlayer`, which also runs when the player changes their avatar from the
8660
+ arcade's own menu without your game asking. A player who closes the picker
8661
+ unchanged produces nothing at all.
8662
+
8663
+ **Your server learns it only when it reads the room again.** A
8664
+ server-authoritative game reads each member's `avatar_icon_id` from the
8665
+ room read (`GET /v1/games/{game_id}/game-rooms/{game_room_id}`, or
8666
+ `gem.room.get()` on a dedicated server); nothing tells the server the
8667
+ player changed. So when `onPlayer` brings a different `avatarIconId`, have
8668
+ your client tell your server, and have the server re-read the room — never
8669
+ trust the id the client sends.
8670
+
8671
+ **Returns.** `true` when the arcade was asked; `false` when it was not and
8672
+ never will be for this call — outside the arcade, before the bridge is
8673
+ up, under an embedder with no avatar picker, or past six asks a minute.
8674
+ It never throws and never waits.
8675
+
8111
8676
  <a id="gem-vibration"></a>
8112
8677
 
8113
8678
  ## Gem.vibration
@@ -8142,6 +8707,32 @@ function onHit() {
8142
8707
 
8143
8708
  **Guides.** [vibration](../vibration.md)
8144
8709
 
8710
+ <a id="gem-controls"></a>
8711
+
8712
+ ## Gem.controls
8713
+
8714
+ ```ts
8715
+ readonly controls: ControlsApi
8716
+ ```
8717
+
8718
+ The player's camera and controls settings — look sensitivity, gamepad look
8719
+ sensitivity, invert look and touch movement, which arrive in
8720
+ `gem.prefs.recommended`. Two things to do with them:
8721
+
8722
+ ```js
8723
+ // 1. Tell the Arcade which ones your game leaves to the player.
8724
+ gem.controls.declare({ lookSensitivity: 'player', invertLookY: 'player' });
8725
+
8726
+ // 2. Turn look input into a camera turn at the player's sensitivity.
8727
+ const look = gem.controls.look();
8728
+ // dx, dy: the pointer's move in CSS px since the last event.
8729
+ const t = look.fromPointer(dx, dy, e.pointerType, camera.pitch);
8730
+ camera.yaw += t.yaw;
8731
+ camera.pitch += t.pitch;
8732
+ ```
8733
+
8734
+ See the guide's "Camera and look controls" section.
8735
+
8145
8736
  <a id="gem-analytics"></a>
8146
8737
 
8147
8738
  ## Gem.analytics
@@ -9686,7 +10277,10 @@ The precomposed `Name#0001`. It contains the name, so it is
9686
10277
  readonly avatarIconId?: string
9687
10278
  ```
9688
10279
 
9689
- The id of the player's portrait item, when they have one.
10280
+ The id of the player's portrait item, when they have one — what a game
10281
+ that draws Arcade avatars keys its own model on. On `gem.player` it
10282
+ changes when the player wears another portrait, and `onPlayer` runs again;
10283
+ see `gem.openAvatarPicker`.
9690
10284
 
9691
10285
  <a id="playeridentity-avatariconurl"></a>
9692
10286
 
@@ -12239,7 +12833,10 @@ The precomposed `Name#0001`. It contains the name, so it is
12239
12833
  readonly avatarIconId?: string
12240
12834
  ```
12241
12835
 
12242
- The id of the player's portrait item, when they have one.
12836
+ The id of the player's portrait item, when they have one — what a game
12837
+ that draws Arcade avatars keys its own model on. On `gem.player` it
12838
+ changes when the player wears another portrait, and `onPlayer` runs again;
12839
+ see `gem.openAvatarPicker`.
12243
12840
 
12244
12841
  <a id="leaderboardentry-avatariconurl"></a>
12245
12842
 
@@ -13681,7 +14278,10 @@ The precomposed `Name#0001`. It contains the name, so it is
13681
14278
  readonly avatarIconId?: string
13682
14279
  ```
13683
14280
 
13684
- The id of the player's portrait item, when they have one.
14281
+ The id of the player's portrait item, when they have one — what a game
14282
+ that draws Arcade avatars keys its own model on. On `gem.player` it
14283
+ changes when the player wears another portrait, and `onPlayer` runs again;
14284
+ see `gem.openAvatarPicker`.
13685
14285
 
13686
14286
  <a id="playerprofile-avatariconurl"></a>
13687
14287
 
@@ -15996,7 +16596,10 @@ The precomposed `Name#0001`. It contains the name, so it is
15996
16596
  readonly avatarIconId?: string
15997
16597
  ```
15998
16598
 
15999
- The id of the player's portrait item, when they have one.
16599
+ The id of the player's portrait item, when they have one — what a game
16600
+ that draws Arcade avatars keys its own model on. On `gem.player` it
16601
+ changes when the player wears another portrait, and `onPlayer` runs again;
16602
+ see `gem.openAvatarPicker`.
16000
16603
 
16001
16604
  <a id="roommember-avatariconurl"></a>
16002
16605
 
@@ -16761,7 +17364,10 @@ The precomposed `Name#0001`. It contains the name, so it is
16761
17364
  readonly avatarIconId?: string
16762
17365
  ```
16763
17366
 
16764
- The id of the player's portrait item, when they have one.
17367
+ The id of the player's portrait item, when they have one — what a game
17368
+ that draws Arcade avatars keys its own model on. On `gem.player` it
17369
+ changes when the player wears another portrait, and `onPlayer` runs again;
17370
+ see `gem.openAvatarPicker`.
16765
17371
 
16766
17372
  <a id="showcasefriendentry-avatariconurl"></a>
16767
17373