Lamp
A switchable lamp with a saved color and state-driven light and glow.
What it demonstrates: Toggle controls · editor actions · lighting.
Run this example
All learning examples appear in a compatible developer world. To export a standalone project, an operator with source access runs this from the Scape repository (choose a new destination under an existing directory):
sh
yarn sdk:starter /path/to/my-lamp --template lamp1
In the exported project:
sh
yarn install
yarn build
yarn dev --origin https://your-scape-host1
2
3
2
3
Follow connection and approval. The exported dev script invokes scape gizmo dev. The kit includes three private archives for the SDK, CLI and MCP server; keep them and the Yarn resolutions together. Third-party dependencies still need installation. See the CLI reference for commands; no public Scape registry install is available yet.
Try changing it
Add a color to the allowed palette. Update the validator and choice controls together.
Source
This code is included directly from the working example when the site builds.
src/definition.ts
ts
/** A standalone SDK lamp. The package chooses the light; Scape renders it. */
import { defineObject, exactKeys, record, requirePayload, ObjectActionError } from '@scape/sdk';
const colors = {
warm: '#ffc078',
blue: '#6baaff',
pink: '#ff80ba',
} as const;
type Color = keyof typeof colors;
/** Shared, saved state; each placed lamp has its own switch and color. */
export interface LampState {
on: boolean;
color: Color;
}
export const lamp = defineObject<LampState>({
type: 'example.lamp',
version: 1,
emoji: '🏮',
label: 'Lamp',
hint: 'switch the light on or choose a color',
initial: () => ({ on: true, color: 'warm' }),
valid: (state): state is LampState =>
record(state) &&
exactKeys(state, ['on', 'color']) &&
typeof state.on === 'boolean' &&
typeof state.color === 'string' &&
Object.prototype.hasOwnProperty.call(colors, state.color),
actions: {
toggle: {
permission: 'participant',
run: (state, payload) => {
requirePayload(payload, []);
return { ...state, on: !state.on };
},
},
color: {
permission: 'editor',
run: (state, payload) => {
requirePayload(payload, ['color']);
if (
typeof payload.color !== 'string' ||
!Object.prototype.hasOwnProperty.call(colors, payload.color)
) {
throw new ObjectActionError(400, 'Choose a lamp color.');
}
return { ...state, color: payload.color as Color };
},
},
},
view: (state, viewer) => ({
title: 'Lamp',
description: '',
controls: [
{
id: 'toggle',
icon: 'toggle',
label: state.on ? 'Turn off' : 'Turn on',
pressed: state.on,
action: { name: 'toggle', payload: {} },
},
...Object.keys(colors).map((color) => ({
id: color,
label: color[0].toUpperCase() + color.slice(1),
pressed: state.color === color,
disabled: !viewer.canEdit,
action: { name: 'color', payload: { color } },
})),
],
}),
// Missing effects mean off. This runs on state changes, never once per frame.
lighting: (state) =>
state.on
? {
light: {
radiusCells: 3,
color: colors[state.color],
intensity: 0.12,
basementIntensity: 0.25,
illumination: 0.7,
},
glow: {
color: colors[state.color],
size: 60,
pulse: {
speed: 0,
amount: 0,
phaseX: 0,
},
opacity: {
base: 0.2,
amount: 0,
speed: 0,
phaseY: 0,
},
},
}
: {},
});
export default lamp;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
src/project.ts
ts
/** Project entry: exports lamp to Scape's SDK loader. */
import { defineProject } from '@scape/sdk';
import { lamp } from './definition.js';
export default defineProject({ objects: [lamp] });1
2
3
4
5
2
3
4
5
