at.atmosynth.module

atmosynth.at

Schema Diff

+41 -15

From

CID
bafyreifevnggceg...
Indexed At
2026-09-08 07:18 UTC
View this version

To

CID
bafyreigkd32vga2...
Indexed At
2026-10-06 13:10 UTC
View this version

Compatibility Analysis

Breaking Changes Detected

9 breaking changes, 4 non-breaking changes.

Breaking Changes (9)
  • KindChanged KindChanged { vertex_id: "at.atmosynth.module#breakpoint.value", old_kind: "unknown", new_kind: "string" }
  • KindChanged KindChanged { vertex_id: "at.atmosynth.module#parameter.default", old_kind: "unknown", new_kind: "string" }
  • KindChanged KindChanged { vertex_id: "at.atmosynth.module#range.max", old_kind: "unknown", new_kind: "string" }
  • KindChanged KindChanged { vertex_id: "at.atmosynth.module#range.min", old_kind: "unknown", new_kind: "string" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#breakpoint.value", sort: "maxLength", value: "200" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.max", sort: "maxLength", value: "200" }
  • ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.min", sort: "maxLength", value: "200" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#parameter.default", sort: "maxLength", value: "200" }
Non-Breaking Changes (4)
  • AddedVertex AddedVertex { vertex_id: "at.atmosynth.module#parameter.scale" }
  • AddedVertex AddedVertex { vertex_id: "at.atmosynth.module:body.patternSource" }
  • AddedEdge AddedEdge { src: "at.atmosynth.module#parameter", tgt: "at.atmosynth.module#parameter.scale", kind: "prop", name: Some("scale") }
  • AddedEdge AddedEdge { src: "at.atmosynth.module:body", tgt: "at.atmosynth.module:body.patternSource", kind: "prop", name: Some("patternSource") }

Migration Guidance

Added Elements

  • AddedVertex { vertex_id: "at.atmosynth.module#parameter.scale" }
  • AddedVertex { vertex_id: "at.atmosynth.module:body.patternSource" }

Constraint Changes

  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#parameter.default", sort: "maxLength", value: "200" }
  • ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#breakpoint.value", sort: "maxLength", value: "200" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.min", sort: "maxLength", value: "200" }
  • ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.max", sort: "maxLength", value: "200" }

Additional Notes

  • Breaking: KindChanged { vertex_id: "at.atmosynth.module#breakpoint.value", old_kind: "unknown", new_kind: "string" }
  • Breaking: KindChanged { vertex_id: "at.atmosynth.module#parameter.default", old_kind: "unknown", new_kind: "string" }
  • Breaking: KindChanged { vertex_id: "at.atmosynth.module#range.max", old_kind: "unknown", new_kind: "string" }
  • Breaking: KindChanged { vertex_id: "at.atmosynth.module#range.min", old_kind: "unknown", new_kind: "string" }
  • Non-breaking: AddedEdge { src: "at.atmosynth.module#parameter", tgt: "at.atmosynth.module#parameter.scale", kind: "prop", name: Some("scale") }
  • Non-breaking: AddedEdge { src: "at.atmosynth.module:body", tgt: "at.atmosynth.module:body.patternSource", kind: "prop", name: Some("patternSource") }
1 1
{
2 2
  "$type": "com.atproto.lexicon.schema",
3 3
  "defs": {
4 4
    "automation": {
5 5
      "description": "A breakpoint envelope scheduled onto an AudioParam. Breakpoints up to and including sustainAt are scheduled on note-on; those after it on note-off.",
6 6
      "properties": {
7 7
        "breakpoints": {
8 8
          "items": {
9 9
            "ref": "#breakpoint",
10 10
            "type": "ref"
11 11
          },
12 12
          "minLength": 1,
13 13
          "type": "array"
14 14
        },
15 15
        "id": {
16 16
          "maxLength": 64,
17 17
          "minLength": 1,
18 18
          "type": "string"
19 19
        },
20 20
        "label": {
21 21
          "maxLength": 200,
22 22
          "type": "string"
23 23
        },
24 24
        "sustainAt": {
25 25
          "description": "Index into breakpoints where the value holds while a note is held. Absent means a one-shot.",
26 26
          "minimum": 0,
27 27
          "type": "integer"
28 28
        },
29 29
        "target": {
30 30
          "ref": "#paramTarget",
31 31
          "type": "ref"
32 32
        }
33 33
      },
34 34
      "required": [
35 35
        "id",
36 36
        "target",
37 37
        "breakpoints"
38 38
      ],
39 39
      "type": "object"
40 40
    },
41 41
    "breakpoint": {
42 42
      "properties": {
43 43
        "curve": {
44 44
          "description": "How the value travels from the previous breakpoint.",
45 45
          "knownValues": [
46 46
            "step",
47 47
            "linear",
48 48
            "exponential"
49 49
          ],
50 50
          "type": "string"
51 51
        },
52 52
        "time": {
53 53
          "description": "Milliseconds from the trigger.",
54 54
          "minimum": 0,
55 55
          "type": "integer"
56 56
        },
57 57
        "value": {
58 -
          "description": "The param's value at this instant. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
59 -
          "type": "unknown"
58 +
          "description": "The param's value at this instant, as its decimal form — \"0.25\", \"1\" — because the ATproto data model has no floating-point type and a PDS refuses one. An automation is numeric by definition, so a reader turns it back into a number without asking anything else.",
59 +
          "maxLength": 200,
60 +
          "type": "string"
60 61
        }
61 62
      },
62 63
      "required": [
63 64
        "time",
64 65
        "value"
65 66
      ],
66 67
      "type": "object"
67 68
    },
68 69
    "connection": {
69 70
      "description": "An audio connection from a node output to either an audio input or a named AudioParam.",
70 71
      "properties": {
71 72
        "from": {
72 73
          "ref": "#source",
73 74
          "type": "ref"
74 75
        },
75 76
        "to": {
76 77
          "ref": "#target",
77 78
          "type": "ref"
78 79
        }
79 80
      },
80 81
      "required": [
81 82
        "from",
82 83
        "to"
83 84
      ],
84 85
      "type": "object"
85 86
    },
86 87
    "lineage": {
87 88
      "description": "The module this one was forked from, and its author.",
88 89
      "properties": {
89 90
        "authorDid": {
90 91
          "format": "did",
91 92
          "type": "string"
92 93
        },
93 94
        "moduleId": {
94 95
          "maxLength": 128,
95 96
          "type": "string"
96 97
        },
97 98
        "uri": {
98 99
          "format": "at-uri",
99 100
          "type": "string"
100 101
        }
101 102
      },
102 103
      "required": [
103 104
        "uri",
104 105
        "authorDid"
105 106
      ],
106 107
      "type": "object"
107 108
    },
108 109
    "main": {
109 110
      "description": "A module, held in its author's own repository.",
110 111
      "key": "tid",
111 112
      "record": {
112 113
        "properties": {
113 114
          "audioInputs": {
114 115
            "description": "Zero or more individually identified audio inputs.",
115 116
            "items": {
116 117
              "ref": "#port",
117 118
              "type": "ref"
118 119
            },
119 120
            "type": "array"
120 121
          },
121 122
          "audioOutputs": {
122 123
            "items": {
123 124
              "ref": "#port",
124 125
              "type": "ref"
125 126
            },
126 127
            "type": "array"
127 128
          },
128 129
          "automation": {
129 130
            "description": "Scheduled AudioParam envelopes. Web Audio has no envelope node, so these are their own structure.",
130 131
            "items": {
131 132
              "ref": "#automation",
132 133
              "type": "ref"
133 134
            },
134 135
            "type": "array"
135 136
          },
136 137
          "connections": {
137 138
            "items": {
138 139
              "ref": "#connection",
139 140
              "type": "ref"
140 141
            },
141 142
            "type": "array"
142 143
          },
143 144
          "createdAt": {
144 145
            "format": "datetime",
145 146
            "type": "string"
146 147
          },
147 148
          "description": {
148 149
            "maxLength": 3000,
149 150
            "type": "string"
150 151
          },
151 152
          "descriptionVersion": {
152 153
            "description": "Version of the module description this record follows. A reader accepts any version at or below its own.",
153 154
            "minimum": 1,
154 155
            "type": "integer"
155 156
          },
156 157
          "forkedFrom": {
157 158
            "ref": "#lineage",
158 159
            "type": "ref"
159 160
          },
160 161
          "midiInputs": {
162 +
            "description": "Zero or more note inputs. A note input carries note events — what is played, and when — from any note source: a sequencer's note output, or the player, which is the on-screen keys, the computer keyboard and every MIDI device at once. \"MIDI\" is the historical name; nothing arriving here need have come from MIDI.",
161 163
            "items": {
162 164
              "ref": "#port",
163 165
              "type": "ref"
164 166
            },
165 167
            "type": "array"
166 168
          },
167 169
          "moduleId": {
168 170
            "description": "Stable identity shared by every published version of this module.",
169 171
            "maxLength": 128,
170 172
            "type": "string"
171 173
          },
172 174
          "name": {
173 175
            "maxLength": 200,
174 176
            "minLength": 1,
175 177
            "type": "string"
176 178
          },
177 179
          "nodes": {
178 180
            "items": {
179 181
              "ref": "#node",
180 182
              "type": "ref"
181 183
            },
182 184
            "type": "array"
183 185
          },
184 186
          "noteOutputs": {
185 -
            "description": "Zero or more note outputs. A note output carries note events — what is played, and when — rather than a signal, so it is wired to another instance's MIDI input rather than to an audio input. A module that declares one is a source of what is played; a module that declares none is what plays it.",
187 +
            "description": "Zero or more note outputs. A note output carries note events — what is played, and when — rather than a signal, so it is wired to another instance's note input (`midiInputs`) rather than to an audio input. A module that declares one is a source of what is played; a module that declares none is what plays it.",
186 188
            "items": {
187 189
              "ref": "#port",
188 190
              "type": "ref"
189 191
            },
190 192
            "type": "array"
191 193
          },
192 194
          "noteSource": {
193 -
            "description": "What this module emits notes by, where it is not made of Web Audio nodes at all. A note source has no `nodes` and no `connections`: it produces note events, and the behaviour that produces them is named here rather than described, because there is no vocabulary of nodes for a thing that makes no sound. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
195 +
            "description": "What this module emits notes by, where it is not made of Web Audio nodes at all. A note source has no `nodes` and no `connections`: it produces note events, and the behaviour that produces them is named here rather than described, because there is no vocabulary of nodes for a thing that makes no sound. `range` emits none of its own: it passes on the notes wired into its input that fall inside the range its instance names (`noteRange` on the patch's instance), each still the note of whatever played it. `transpose` and `assign` emit none of their own either: they pass on every note wired into their input at another pitch — moved by the semitones the instance names (`transpose`), or at the one note it names (`assignedNote`) — each still the note of whatever played it, and starting and stopping when it did. `oneshot` emits none of its own either: it passes on every note wired into its input lasting as many milliseconds as the instance names (`noteLength`), however long whatever played it held it — each still the note of whatever played it, at its own pitch, and starting when it did. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
194 196
            "knownValues": [
195 197
              "keyboard",
196 -
              "sequencer"
198 +
              "sequencer",
199 +
              "range",
200 +
              "transpose",
201 +
              "assign",
202 +
              "oneshot"
197 203
            ],
198 204
            "maxLength": 64,
199 205
            "type": "string"
200 206
          },
201 207
          "parameters": {
202 208
            "items": {
203 209
              "ref": "#parameter",
204 210
              "type": "ref"
205 211
            },
206 212
            "type": "array"
207 213
          },
214 +
          "patternSource": {
215 +
            "description": "What this module chooses a sequencer's loops by, where it is not made of Web Audio nodes at all. A pattern source emits neither sound nor notes: it is wired to a sequencer's pattern input and decides which of that sequencer's loops is played, and when. Like a note source it has no `nodes` and no `connections`, and its behaviour is named here rather than described. A module is a note source or a pattern source, never both. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
216 +
            "knownValues": [
217 +
              "arranger",
218 +
              "scenes"
219 +
            ],
220 +
            "maxLength": 64,
221 +
            "type": "string"
222 +
          },
208 223
          "sampleSlots": {
209 224
            "items": {
210 225
              "ref": "#sampleSlot",
211 226
              "type": "ref"
212 227
            },
213 228
            "type": "array"
214 229
          },
215 230
          "versionKey": {
216 -
            "description": "This record's own key within the series. Informational; the record key is authoritative.",
231 +
            "description": "The record key this version is published under, repeated in the record so a reader holding only the record knows which version it is. A patch's `moduleRef.versionKey` is this value, and it is the record key a reader fetches: a record whose `versionKey` differs from its own key is pinned by the key and misdescribed by this field.",
217 232
            "maxLength": 128,
218 233
            "type": "string"
219 234
          }
220 235
        },
221 236
        "required": [
222 237
          "descriptionVersion",
223 238
          "moduleId",
224 239
          "name",
225 240
          "description",
226 241
          "audioInputs",
227 242
          "audioOutputs",
228 243
          "midiInputs",
229 244
          "nodes",
230 245
          "connections",
231 246
          "createdAt"
232 247
        ],
233 248
        "type": "object"
234 249
      },
235 250
      "type": "record"
236 251
    },
237 252
    "node": {
238 253
      "description": "One Web Audio node. `kind` is drawn from a closed vocabulary; `options` are its construction-time settings, which are not AudioParams and cannot be modulated.",
239 254
      "properties": {
240 255
        "id": {
241 256
          "maxLength": 64,
242 257
          "minLength": 1,
243 258
          "type": "string"
244 259
        },
245 260
        "kind": {
246 261
          "maxLength": 64,
247 262
          "minLength": 1,
248 263
          "type": "string"
249 264
        },
250 265
        "options": {
251 -
          "description": "The node's settings, as its kind defines them. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
266 +
          "description": "The node's settings, as its kind defines them. This is an object — which is the whole of what `unknown` means here — so the data model applies inside it: a whole number is an ordinary integer, and a number that is not a whole number is carried as its decimal string, \"0.5\" rather than 0.5, because the ATproto data model has no floating-point type and a PDS refuses one. That is the one difference from the fields this description types as strings, which have nowhere to put an integer.",
252 267
          "type": "unknown"
253 268
        }
254 269
      },
255 270
      "required": [
256 271
        "id",
257 272
        "kind"
258 273
      ],
259 274
      "type": "object"
260 275
    },
261 276
    "paramTarget": {
262 277
      "properties": {
263 278
        "node": {
264 279
          "maxLength": 64,
265 280
          "minLength": 1,
266 281
          "type": "string"
267 282
        },
268 283
        "param": {
269 284
          "maxLength": 64,
270 285
          "minLength": 1,
271 286
          "type": "string"
272 287
        }
273 288
      },
274 289
      "required": [
275 290
        "node",
276 291
        "param"
277 292
      ],
278 293
      "type": "object"
279 294
    },
280 295
    "parameter": {
281 296
      "description": "A parameter the module exposes for a patch to set. Each target reaches an AudioParam, a construction option, or one field of an automation breakpoint.",
282 297
      "properties": {
283 298
        "default": {
284 -
          "description": "The value used where a patch sets none. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number. Where this parameter declares a closed value set, it is one of those strings.",
285 -
          "type": "unknown"
299 +
          "description": "The value used where a patch sets none, always as text. A number is its decimal form — \"0.5\", and \"2\" for a whole one — because the ATproto data model has no floating-point type and a string is the only type that carries both a number and a word. Where this parameter declares a closed value set, it is one of those strings instead, and a label that looks like a number stays the label it is.",
300 +
          "maxLength": 200,
301 +
          "type": "string"
286 302
        },
287 303
        "id": {
288 304
          "maxLength": 64,
289 305
          "minLength": 1,
290 306
          "type": "string"
291 307
        },
292 308
        "label": {
293 309
          "maxLength": 200,
294 310
          "minLength": 1,
295 311
          "type": "string"
296 312
        },
297 313
        "range": {
298 314
          "ref": "#range",
299 315
          "type": "ref"
300 316
        },
317 +
        "scale": {
318 +
          "description": "How a slider or a MIDI control moves through the range. linear moves by equal differences; log moves by equal ratios, which is how a frequency is heard, so the middle of a 20 to 20000 sweep is about 632 rather than about 10000. log needs a range above zero at both ends. Absent is linear. A parameter with a closed value set has no scale.",
319 +
          "knownValues": [
320 +
            "linear",
321 +
            "log"
322 +
          ],
323 +
          "type": "string"
324 +
        },
301 325
        "targets": {
302 326
          "items": {
303 327
            "ref": "#parameterTarget",
304 328
            "type": "ref"
305 329
          },
306 330
          "minLength": 1,
307 331
          "type": "array"
308 332
        },
309 333
        "values": {
310 -
          "description": "A closed value set, where the parameter is not numeric.",
334 +
          "description": "A closed value set, where the parameter is not numeric. A construction option the parameter targets is given the chosen value itself; an AudioParam it targets is given the chosen value's position in this set, counting from zero, which is how a worklet offers a choice its own code interprets.",
311 335
          "items": {
312 336
            "maxLength": 200,
313 337
            "type": "string"
314 338
          },
315 339
          "type": "array"
316 340
        }
317 341
      },
318 342
      "required": [
319 343
        "id",
320 344
        "label",
321 345
        "targets"
322 346
      ],
323 347
      "type": "object"
324 348
    },
325 349
    "parameterTarget": {
326 350
      "description": "One of: {node, param}, {node, option}, or {automation, breakpoint, field}.",
327 351
      "properties": {
328 352
        "automation": {
329 353
          "maxLength": 64,
330 354
          "type": "string"
331 355
        },
332 356
        "breakpoint": {
333 357
          "minimum": 0,
334 358
          "type": "integer"
335 359
        },
336 360
        "field": {
337 361
          "knownValues": [
338 362
            "time",
339 363
            "value"
340 364
          ],
341 365
          "type": "string"
342 366
        },
343 367
        "node": {
344 368
          "maxLength": 64,
345 369
          "type": "string"
346 370
        },
347 371
        "option": {
348 372
          "maxLength": 64,
349 373
          "type": "string"
350 374
        },
351 375
        "param": {
352 376
          "maxLength": 64,
353 377
          "type": "string"
354 378
        }
355 379
      },
356 380
      "type": "object"
357 381
    },
358 382
    "port": {
359 383
      "description": "One individually identified audio or MIDI boundary of the module.",
360 384
      "properties": {
361 385
        "id": {
362 386
          "maxLength": 64,
363 387
          "minLength": 1,
364 388
          "type": "string"
365 389
        },
366 390
        "label": {
367 391
          "maxLength": 200,
368 392
          "type": "string"
369 393
        }
370 394
      },
371 395
      "required": [
372 396
        "id"
373 397
      ],
374 398
      "type": "object"
375 399
    },
376 400
    "range": {
377 401
      "properties": {
378 402
        "max": {
379 -
          "description": "The high bound. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
380 -
          "type": "unknown"
403 +
          "description": "The high bound, as its decimal form — \"20000\", \"0.5\" — because the ATproto data model has no floating-point type and a PDS refuses one. A range is numeric by definition, so a reader turns it back into a number without asking anything else.",
404 +
          "maxLength": 200,
405 +
          "type": "string"
381 406
        },
382 407
        "min": {
383 -
          "description": "The low bound. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
384 -
          "type": "unknown"
408 +
          "description": "The low bound, as its decimal form — \"0.0001\", \"-1\" — because the ATproto data model has no floating-point type and a PDS refuses one. A range is numeric by definition, so a reader turns it back into a number without asking anything else.",
409 +
          "maxLength": 200,
410 +
          "type": "string"
385 411
        }
386 412
      },
387 413
      "required": [
388 414
        "min",
389 415
        "max"
390 416
      ],
391 417
      "type": "object"
392 418
    },
393 419
    "sampleRef": {
394 420
      "description": "A sample blob and the account that holds it.",
395 421
      "properties": {
396 422
        "blob": {
397 423
          "accept": [
398 424
            "audio/wav"
399 425
          ],
400 -
          "maxSize": 2097152,
426 +
          "maxSize": 10485760,
401 427
          "type": "blob"
402 428
        },
403 429
        "ownerDid": {
404 430
          "format": "did",
405 431
          "type": "string"
406 432
        }
407 433
      },
408 434
      "required": [
409 435
        "blob",
410 436
        "ownerDid"
411 437
      ],
412 438
      "type": "object"
413 439
    },
414 440
    "sampleSlot": {
415 441
      "description": "A named slot a patch instance may fill with its own sample. `node` names a node in this graph and `property` the thing the sample feeds.",
416 442
      "properties": {
417 443
        "default": {
418 444
          "ref": "#sampleRef",
419 445
          "type": "ref"
420 446
        },
421 447
        "id": {
422 448
          "maxLength": 64,
423 449
          "minLength": 1,
424 450
          "type": "string"
425 451
        },
426 452
        "label": {
427 453
          "maxLength": 200,
428 454
          "type": "string"
429 455
        },
430 456
        "node": {
431 457
          "maxLength": 64,
432 458
          "minLength": 1,
433 459
          "type": "string"
434 460
        },
435 461
        "property": {
436 462
          "knownValues": [
437 463
            "buffer"
438 464
          ],
439 465
          "type": "string"
440 466
        }
441 467
      },
442 468
      "required": [
443 469
        "id",
444 470
        "node",
445 471
        "property"
446 472
      ],
447 473
      "type": "object"
448 474
    },
449 475
    "source": {
450 476
      "properties": {
451 477
        "node": {
452 478
          "maxLength": 64,
453 479
          "minLength": 1,
454 480
          "type": "string"
455 481
        },
456 482
        "output": {
457 483
          "minimum": 0,
458 484
          "type": "integer"
459 485
        }
460 486
      },
461 487
      "required": [
462 488
        "node"
463 489
      ],
464 490
      "type": "object"
465 491
    },
466 492
    "target": {
467 493
      "description": "Exactly one of `input` or `param` is present. `param` is modulation: a node output reaching a named AudioParam.",
468 494
      "properties": {
469 495
        "input": {
470 496
          "minimum": 0,
471 497
          "type": "integer"
472 498
        },
473 499
        "node": {
474 500
          "maxLength": 64,
475 501
          "minLength": 1,
476 502
          "type": "string"
477 503
        },
478 504
        "param": {
479 505
          "maxLength": 64,
480 506
          "type": "string"
481 507
        }
482 508
      },
483 509
      "required": [
484 510
        "node"
485 511
      ],
486 512
      "type": "object"
487 513
    }
488 514
  },
489 515
  "description": "A reusable synth module: Web Audio nodes wired together, with declared audio and MIDI boundaries, exposed parameters, automation and sample slots. Each published version is its own record; records sharing a moduleId form a series.",
490 516
  "id": "at.atmosynth.module",
491 517
  "lexicon": 1
492 518
}

Compare Other Versions

Lexicon Garden

@