{"version":3,"file":"main.js","sources":["../../node_modules/idb-keyval/dist/esm/index.js","../../node_modules/safari-14-idb-fix/dist/esm/index.js","../../node_modules/webaudio-tinysynth/webaudio-tinysynth.js","../../src/value.ts","../../src/shader/time.ts","../../src/controller/audio.ts","../../src/input/keyboard.ts","../../src/input/gamepad.ts","../../src/config.ts","../../src/steam.ts","../../src/input/browser.ts","../../src/render.ts","../../src/input/mouse.ts","../../src/controller/controls.ts","../../src/input/joints.ts","../../src/input/phony.ts","../../src/buffer/atomic.ts","../../src/buffer/animation.ts","../../src/buffer/phys.ts","../../src/buffer/traits.ts","../../src/fate/weave.ts","../../src/buffer/cage.ts","../../src/buffer/fate.ts","../../src/buffer/impact.ts","../../src/buffer/matter.ts","../../src/buffer/size.ts","../../src/buffer/spacetime.ts","../../src/buffer/vec3.ts","../../src/buffer/thrust.ts","../../src/buffer/universal.ts","../../src/buffer/velocity.ts","../../src/magica.ts","../../src/input/load.ts","../../src/input/xr.ts","../../src/system/enum.ts","../../src/system/sys.ts","../../src/buffer/input.ts","../../src/buffer/sensed.ts","../../src/system/yggdrasil.ts","../../src/system/system.ts","../../src/realm.ts","../../src/controller/smooth.ts","../../src/controller/fps.ts","../../src/input/poses.ts","../../src/controller/spell/swipe.ts","../../src/controller/spell/walk.ts","../../src/controller/spell/snapshot.ts","../../src/controller/hands.ts","../../src/VRButton.ts","../../src/fate/rez/hand-joints.ts","../../node_modules/svelte/internal/index.mjs","../../node_modules/file-saver/dist/FileSaver.min.js","../../src/fate/editor.ts","../../src/notify.ts","../../src/input/save.ts","../../src/fate/nav.ts","../../src/fate/Box.svelte","../../src/fate/color.ts","../../src/fate/evar/Pattern.svelte","../../src/fate/Node.svelte","../../src/fate/Fate.svelte","../../src/fate/evar/String.svelte","../../src/fate/evar/Number.svelte","../../src/fate/evar/Normal.svelte","../../src/fate/evar/Time.svelte","../../src/fate/evar/Noise.svelte","../../src/fate/evar/Patterns.svelte","../../src/fate/Modal.svelte","../../src/controller/copypaste.ts","../../src/input/file.ts","../../src/fate/Score.svelte","../../src/fate/Landing.svelte","../../src/fate/Theiaology.svelte","../../src/reaction.ts","../../src/main.ts"],"sourcesContent":["import safariFix from 'safari-14-idb-fix';\n\nfunction promisifyRequest(request) {\n return new Promise((resolve, reject) => {\n // @ts-ignore - file size hacks\n request.oncomplete = request.onsuccess = () => resolve(request.result);\n // @ts-ignore - file size hacks\n request.onabort = request.onerror = () => reject(request.error);\n });\n}\nfunction createStore(dbName, storeName) {\n const dbp = safariFix().then(() => {\n const request = indexedDB.open(dbName);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName);\n return promisifyRequest(request);\n });\n return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));\n}\nlet defaultGetStoreFunc;\nfunction defaultGetStore() {\n if (!defaultGetStoreFunc) {\n defaultGetStoreFunc = createStore('keyval-store', 'keyval');\n }\n return defaultGetStoreFunc;\n}\n/**\n * Get a value by its key.\n *\n * @param key\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction get(key, customStore = defaultGetStore()) {\n return customStore('readonly', (store) => promisifyRequest(store.get(key)));\n}\n/**\n * Set a value with a key.\n *\n * @param key\n * @param value\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction set(key, value, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.put(value, key);\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Set multiple values at once. This is faster than calling set() multiple times.\n * It's also atomic – if one of the pairs can't be added, none will be added.\n *\n * @param entries Array of entries, where each entry is an array of `[key, value]`.\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction setMany(entries, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n entries.forEach((entry) => store.put(entry[1], entry[0]));\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Get multiple values by their keys\n *\n * @param keys\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction getMany(keys, customStore = defaultGetStore()) {\n return customStore('readonly', (store) => Promise.all(keys.map((key) => promisifyRequest(store.get(key)))));\n}\n/**\n * Update a value. This lets you see the old value and update it as an atomic operation.\n *\n * @param key\n * @param updater A callback that takes the old value and returns a new value.\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction update(key, updater, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => \n // Need to create the promise manually.\n // If I try to chain promises, the transaction closes in browsers\n // that use a promise polyfill (IE10/11).\n new Promise((resolve, reject) => {\n store.get(key).onsuccess = function () {\n try {\n store.put(updater(this.result), key);\n resolve(promisifyRequest(store.transaction));\n }\n catch (err) {\n reject(err);\n }\n };\n }));\n}\n/**\n * Delete a particular key from the store.\n *\n * @param key\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction del(key, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.delete(key);\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Clear all values in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction clear(customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.clear();\n return promisifyRequest(store.transaction);\n });\n}\nfunction eachCursor(customStore, callback) {\n return customStore('readonly', (store) => {\n // This would be store.getAllKeys(), but it isn't supported by Edge or Safari.\n // And openKeyCursor isn't supported by Safari.\n store.openCursor().onsuccess = function () {\n if (!this.result)\n return;\n callback(this.result);\n this.result.continue();\n };\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Get all keys in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction keys(customStore = defaultGetStore()) {\n const items = [];\n return eachCursor(customStore, (cursor) => items.push(cursor.key)).then(() => items);\n}\n/**\n * Get all values in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction values(customStore = defaultGetStore()) {\n const items = [];\n return eachCursor(customStore, (cursor) => items.push(cursor.value)).then(() => items);\n}\n/**\n * Get all entries in the store. Each entry is an array of `[key, value]`.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction entries(customStore = defaultGetStore()) {\n const items = [];\n return eachCursor(customStore, (cursor) => items.push([cursor.key, cursor.value])).then(() => items);\n}\n\nexport { clear, createStore, del, entries, get, getMany, keys, promisifyRequest, set, setMany, update, values };\n","/**\n * https://bugs.webkit.org/show_bug.cgi?id=226547\n * Safari has a horrible bug where IDB requests can hang while the browser is starting up.\n * The only solution is to keep nudging it until it's awake.\n * This probably creates garbage, but garbage is better than totally failing.\n */\nfunction idbReady() {\n const isSafari = !navigator.userAgentData &&\n /Safari\\//.test(navigator.userAgent) &&\n !/Chrom(e|ium)\\//.test(navigator.userAgent);\n // No point putting other browsers or older versions of Safari through this mess.\n if (!isSafari || !indexedDB.databases)\n return Promise.resolve();\n let intervalId;\n return new Promise((resolve) => {\n const tryIdb = () => indexedDB.databases().finally(resolve);\n intervalId = setInterval(tryIdb, 100);\n tryIdb();\n }).finally(() => clearInterval(intervalId));\n}\n\nexport default idbReady;\n","( function(window){\r\n\"use strict\";\r\n\r\nfunction WebAudioTinySynthCore(target) {\r\n Object.assign(target,{\r\n properties:{\r\n masterVol: {type:Number, value:0.5, observer:\"setMasterVol\"},\r\n reverbLev: {type:Number, value:0.3, observer:\"setReverbLev\"},\r\n quality: {type:Number, value:1, observer:\"setQuality\"},\r\n debug: {type:Number, value:0},\r\n src: {type:String, value:null, observer:\"loadMIDIfromSrc\"},\r\n loop: {type:Number, value:0},\r\n internalcontext: {type:Number, value:1},\r\n tsmode: {type:Number, value:0},\r\n voices: {type:Number, value:64},\r\n useReverb: {type:Number, value:1},\r\n /*@@gui*/\r\n width: {type:String, value:\"300px\", observer:\"layout\"},\r\n height: {type:String, value:\"32px\", observer:\"layout\"},\r\n graph: {type:Number, value:1},\r\n disabledrop:{type:Number, value:0},\r\n perfmon: {type:Number, value:0},\r\n /*@@guiEND*/\r\n },\r\n /*@@gui*/\r\n layout:(()=>{\r\n this.canvas.style.width=this.width;\r\n this.canvas.style.height=this.height; \r\n }),\r\n /*@@guiEND*/\r\n program:[\r\n// 1-8 : Piano\r\n {name:\"Acoustic Grand Piano\"}, {name:\"Bright Acoustic Piano\"},\r\n {name:\"Electric Grand Piano\"}, {name:\"Honky-tonk Piano\"},\r\n {name:\"Electric Piano 1\"}, {name:\"Electric Piano 2\"},\r\n {name:\"Harpsichord\"}, {name:\"Clavi\"},\r\n/* 9-16 : Chromatic Perc*/\r\n {name:\"Celesta\"}, {name:\"Glockenspiel\"},\r\n {name:\"Music Box\"}, {name:\"Vibraphone\"},\r\n {name:\"Marimba\"}, {name:\"Xylophone\"},\r\n {name:\"Tubular Bells\"}, {name:\"Dulcimer\"},\r\n/* 17-24 : Organ */\r\n {name:\"Drawbar Organ\"}, {name:\"Percussive Organ\"},\r\n {name:\"Rock Organ\"}, {name:\"Church Organ\"},\r\n {name:\"Reed Organ\"}, {name:\"Accordion\"},\r\n {name:\"Harmonica\"}, {name:\"Tango Accordion\"},\r\n/* 25-32 : Guitar */\r\n {name:\"Acoustic Guitar (nylon)\"}, {name:\"Acoustic Guitar (steel)\"},\r\n {name:\"Electric Guitar (jazz)\"}, {name:\"Electric Guitar (clean)\"},\r\n {name:\"Electric Guitar (muted)\"}, {name:\"Overdriven Guitar\"},\r\n {name:\"Distortion Guitar\"}, {name:\"Guitar harmonics\"},\r\n/* 33-40 : Bass */\r\n {name:\"Acoustic Bass\"}, {name:\"Electric Bass (finger)\"},\r\n {name:\"Electric Bass (pick)\"}, {name:\"Fretless Bass\"},\r\n {name:\"Slap Bass 1\"}, {name:\"Slap Bass 2\"},\r\n {name:\"Synth Bass 1\"}, {name:\"Synth Bass 2\"},\r\n/* 41-48 : Strings */\r\n {name:\"Violin\"}, {name:\"Viola\"},\r\n {name:\"Cello\"}, {name:\"Contrabass\"},\r\n {name:\"Tremolo Strings\"}, {name:\"Pizzicato Strings\"},\r\n {name:\"Orchestral Harp\"}, {name:\"Timpani\"},\r\n/* 49-56 : Ensamble */\r\n {name:\"String Ensemble 1\"}, {name:\"String Ensemble 2\"},\r\n {name:\"SynthStrings 1\"}, {name:\"SynthStrings 2\"},\r\n {name:\"Choir Aahs\"}, {name:\"Voice Oohs\"},\r\n {name:\"Synth Voice\"}, {name:\"Orchestra Hit\"},\r\n/* 57-64 : Brass */\r\n {name:\"Trumpet\"}, {name:\"Trombone\"},\r\n {name:\"Tuba\"}, {name:\"Muted Trumpet\"},\r\n {name:\"French Horn\"}, {name:\"Brass Section\"},\r\n {name:\"SynthBrass 1\"}, {name:\"SynthBrass 2\"},\r\n/* 65-72 : Reed */\r\n {name:\"Soprano Sax\"}, {name:\"Alto Sax\"},\r\n {name:\"Tenor Sax\"}, {name:\"Baritone Sax\"},\r\n {name:\"Oboe\"}, {name:\"English Horn\"},\r\n {name:\"Bassoon\"}, {name:\"Clarinet\"},\r\n/* 73-80 : Pipe */\r\n {name:\"Piccolo\"}, {name:\"Flute\"},\r\n {name:\"Recorder\"}, {name:\"Pan Flute\"},\r\n {name:\"Blown Bottle\"}, {name:\"Shakuhachi\"},\r\n {name:\"Whistle\"}, {name:\"Ocarina\"},\r\n/* 81-88 : SynthLead */\r\n {name:\"Lead 1 (square)\"}, {name:\"Lead 2 (sawtooth)\"},\r\n {name:\"Lead 3 (calliope)\"}, {name:\"Lead 4 (chiff)\"},\r\n {name:\"Lead 5 (charang)\"}, {name:\"Lead 6 (voice)\"},\r\n {name:\"Lead 7 (fifths)\"}, {name:\"Lead 8 (bass + lead)\"},\r\n/* 89-96 : SynthPad */\r\n {name:\"Pad 1 (new age)\"}, {name:\"Pad 2 (warm)\"},\r\n {name:\"Pad 3 (polysynth)\"}, {name:\"Pad 4 (choir)\"},\r\n {name:\"Pad 5 (bowed)\"}, {name:\"Pad 6 (metallic)\"},\r\n {name:\"Pad 7 (halo)\"}, {name:\"Pad 8 (sweep)\"},\r\n/* 97-104 : FX */\r\n {name:\"FX 1 (rain)\"}, {name:\"FX 2 (soundtrack)\"},\r\n {name:\"FX 3 (crystal)\"}, {name:\"FX 4 (atmosphere)\"},\r\n {name:\"FX 5 (brightness)\"}, {name:\"FX 6 (goblins)\"},\r\n {name:\"FX 7 (echoes)\"}, {name:\"FX 8 (sci-fi)\"},\r\n/* 105-112 : Ethnic */\r\n {name:\"Sitar\"}, {name:\"Banjo\"},\r\n {name:\"Shamisen\"}, {name:\"Koto\"},\r\n {name:\"Kalimba\"}, {name:\"Bag pipe\"},\r\n {name:\"Fiddle\"}, {name:\"Shanai\"},\r\n/* 113-120 : Percussive */\r\n {name:\"Tinkle Bell\"}, {name:\"Agogo\"},\r\n {name:\"Steel Drums\"}, {name:\"Woodblock\"},\r\n {name:\"Taiko Drum\"}, {name:\"Melodic Tom\"},\r\n {name:\"Synth Drum\"}, {name:\"Reverse Cymbal\"},\r\n/* 121-128 : SE */\r\n {name:\"Guitar Fret Noise\"}, {name:\"Breath Noise\"},\r\n {name:\"Seashore\"}, {name:\"Bird Tweet\"},\r\n {name:\"Telephone Ring\"}, {name:\"Helicopter\"},\r\n {name:\"Applause\"}, {name:\"Gunshot\"},\r\n ],\r\n drummap:[\r\n// 35\r\n {name:\"Acoustic Bass Drum\"}, {name:\"Bass Drum 1\"}, {name:\"Side Stick\"}, {name:\"Acoustic Snare\"},\r\n {name:\"Hand Clap\"}, {name:\"Electric Snare\"}, {name:\"Low Floor Tom\"}, {name:\"Closed Hi Hat\"},\r\n {name:\"High Floor Tom\"}, {name:\"Pedal Hi-Hat\"}, {name:\"Low Tom\"}, {name:\"Open Hi-Hat\"},\r\n {name:\"Low-Mid Tom\"}, {name:\"Hi-Mid Tom\"}, {name:\"Crash Cymbal 1\"}, {name:\"High Tom\"},\r\n {name:\"Ride Cymbal 1\"}, {name:\"Chinese Cymbal\"}, {name:\"Ride Bell\"}, {name:\"Tambourine\"},\r\n {name:\"Splash Cymbal\"}, {name:\"Cowbell\"}, {name:\"Crash Cymbal 2\"}, {name:\"Vibraslap\"},\r\n {name:\"Ride Cymbal 2\"}, {name:\"Hi Bongo\"}, {name:\"Low Bongo\"}, {name:\"Mute Hi Conga\"},\r\n {name:\"Open Hi Conga\"}, {name:\"Low Conga\"}, {name:\"High Timbale\"}, {name:\"Low Timbale\"},\r\n {name:\"High Agogo\"}, {name:\"Low Agogo\"}, {name:\"Cabasa\"}, {name:\"Maracas\"},\r\n {name:\"Short Whistle\"}, {name:\"Long Whistle\"}, {name:\"Short Guiro\"}, {name:\"Long Guiro\"},\r\n {name:\"Claves\"}, {name:\"Hi Wood Block\"}, {name:\"Low Wood Block\"}, {name:\"Mute Cuica\"},\r\n {name:\"Open Cuica\"}, {name:\"Mute Triangle\"}, {name:\"Open Triangle\"},\r\n ],\r\n program1:[\r\n // 1-8 : Piano\r\n [{w:\"sine\",v:.4,d:0.7,r:0.1,},{w:\"triangle\",v:3,d:0.7,s:0.1,g:1,a:0.01,k:-1.2}],\r\n [{w:\"triangle\",v:0.4,d:0.7,r:0.1,},{w:\"triangle\",v:4,t:3,d:0.4,s:0.1,g:1,k:-1,a:0.01,}],\r\n [{w:\"sine\",d:0.7,r:0.1,},{w:\"triangle\",v:4,f:2,d:0.5,s:0.5,g:1,k:-1}],\r\n [{w:\"sine\",d:0.7,v:0.2,},{w:\"triangle\",v:4,t:3,f:2,d:0.3,g:1,k:-1,a:0.01,s:0.5,}],\r\n [{w:\"sine\",v:0.35,d:0.7,},{w:\"sine\",v:3,t:7,f:1,d:1,s:1,g:1,k:-.7}],\r\n [{w:\"sine\",v:0.35,d:0.7,},{w:\"sine\",v:8,t:7,f:1,d:0.5,s:1,g:1,k:-.7}],\r\n [{w:\"sawtooth\",v:0.34,d:2,},{w:\"sine\",v:8,f:0.1,d:2,s:1,r:2,g:1,}],\r\n [{w:\"triangle\",v:0.34,d:1.5,},{w:\"square\",v:6,f:0.1,d:1.5,s:0.5,r:2,g:1,}],\r\n /* 9-16 : Chromatic Perc*/\r\n [{w:\"sine\",d:0.3,r:0.3,},{w:\"sine\",v:7,t:11,d:0.03,g:1,}],\r\n [{w:\"sine\",d:0.3,r:0.3,},{w:\"sine\",v:11,t:6,d:0.2,s:0.4,g:1,}],\r\n [{w:\"sine\",v:0.2,d:0.3,r:0.3,},{w:\"sine\",v:11,t:5,d:0.1,s:0.4,g:1,}],\r\n [{w:\"sine\",v:0.2,d:0.6,r:0.6,},{w:\"triangle\",v:11,t:5,f:1,s:0.5,g:1,}],\r\n [{w:\"sine\",v:0.3,d:0.2,r:0.2,},{w:\"sine\",v:6,t:5,d:0.02,g:1,}],\r\n [{w:\"sine\",v:0.3,d:0.2,r:0.2,},{w:\"sine\",v:7,t:11,d:0.03,g:1,}],\r\n [{w:\"sine\",v:0.2,d:1,r:1,},{w:\"sine\",v:11,t:3.5,d:1,r:1,g:1,}],\r\n [{w:\"triangle\",v:0.2,d:0.5,r:0.2,},{w:\"sine\",v:6,t:2.5,d:0.2,s:0.1,r:0.2,g:1,}],\r\n /* 17-24 : Organ */\r\n [{w:\"w9999\",v:0.22,s:0.9,},{w:\"w9999\",v:0.22,t:2,f:2,s:0.9,}],\r\n [{w:\"w9999\",v:0.2,s:1,},{w:\"sine\",v:11,t:6,f:2,s:0.1,g:1,h:0.006,r:0.002,d:0.002,},{w:\"w9999\",v:0.2,t:2,f:1,h:0,s:1,}],\r\n [{w:\"w9999\",v:0.2,d:0.1,s:0.9,},{w:\"w9999\",v:0.25,t:4,f:2,s:0.5,}],\r\n [{w:\"w9999\",v:0.3,a:0.04,s:0.9,},{w:\"w9999\",v:0.2,t:8,f:2,a:0.04,s:0.9,}],\r\n [{w:\"sine\",v:0.2,a:0.02,d:0.05,s:1,},{w:\"sine\",v:6,t:3,f:1,a:0.02,d:0.05,s:1,g:1,}],\r\n [{w:\"triangle\",v:0.2,a:0.02,d:0.05,s:0.8,},{w:\"square\",v:7,t:3,f:1,d:0.05,s:1.5,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:0.2,s:0.5,},{w:\"square\",v:1,d:0.03,s:2,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:0.1,s:0.8,},{w:\"square\",v:1,a:0.3,d:0.1,s:2,g:1,}],\r\n /* 25-32 : Guitar */\r\n [{w:\"sine\",v:0.3,d:0.5,f:1,},{w:\"triangle\",v:5,t:3,f:-1,d:1,s:0.1,g:1,}],\r\n [{w:\"sine\",v:0.4,d:0.6,f:1,},{w:\"triangle\",v:12,t:3,d:0.6,s:0.1,g:1,f:-1,}],\r\n [{w:\"triangle\",v:0.3,d:1,f:1,},{w:\"triangle\",v:6,f:-1,d:0.4,s:0.5,g:1,t:3,}],\r\n [{w:\"sine\",v:0.3,d:1,f:-1,},{w:\"triangle\",v:11,f:1,d:0.4,s:0.5,g:1,t:3,}],\r\n [{w:\"sine\",v:0.4,d:0.1,r:0.01},{w:\"sine\",v:7,g:1,}],\r\n [{w:\"triangle\",v:0.4,d:1,f:1,},{w:\"square\",v:4,f:-1,d:1,s:0.7,g:1,}],//[{w:\"triangle\",v:0.35,d:1,f:1,},{w:\"square\",v:7,f:-1,d:0.3,s:0.5,g:1,}],\r\n [{w:\"triangle\",v:0.35,d:1,f:1,},{w:\"square\",v:7,f:-1,d:0.3,s:0.5,g:1,}],//[{w:\"triangle\",v:0.4,d:1,f:1,},{w:\"square\",v:4,f:-1,d:1,s:0.7,g:1,}],//[{w:\"triangle\",v:0.4,d:1,},{w:\"square\",v:4,f:2,d:1,s:0.7,g:1,}],\r\n [{w:\"sine\",v:0.2,t:1.5,a:0.005,h:0.2,d:0.6,},{w:\"sine\",v:11,t:5,f:2,d:1,s:0.5,g:1,}],\r\n /* 33-40 : Bass */\r\n [{w:\"sine\",d:0.3,},{w:\"sine\",v:4,t:3,d:1,s:1,g:1,}],\r\n [{w:\"sine\",d:0.3,},{w:\"sine\",v:4,t:3,d:1,s:1,g:1,}],\r\n [{w:\"w9999\",d:0.3,v:0.7,s:0.5,},{w:\"sawtooth\",v:1.2,d:0.02,s:0.5,g:1,h:0,r:0.02,}],\r\n [{w:\"sine\",d:0.3,},{w:\"sine\",v:4,t:3,d:1,s:1,g:1,}],\r\n [{w:\"triangle\",v:0.3,t:2,d:1,},{w:\"triangle\",v:15,t:2.5,d:0.04,s:0.1,g:1,}],\r\n [{w:\"triangle\",v:0.3,t:2,d:1,},{w:\"triangle\",v:15,t:2.5,d:0.04,s:0.1,g:1,}],\r\n [{w:\"triangle\",d:0.7,},{w:\"square\",v:0.4,t:0.5,f:1,d:0.2,s:10,g:1,}],\r\n [{w:\"triangle\",d:0.7,},{w:\"square\",v:0.4,t:0.5,f:1,d:0.2,s:10,g:1,}],\r\n /* 41-48 : Strings */\r\n [{w:\"sawtooth\",v:0.4,a:0.1,d:11,},{w:\"sine\",v:5,d:11,s:0.2,g:1,}],\r\n [{w:\"sawtooth\",v:0.4,a:0.1,d:11,},{w:\"sine\",v:5,d:11,s:0.2,g:1,}],\r\n [{w:\"sawtooth\",v:0.4,a:0.1,d:11,},{w:\"sine\",v:5,t:0.5,d:11,s:0.2,g:1,}],\r\n [{w:\"sawtooth\",v:0.4,a:0.1,d:11,},{w:\"sine\",v:5,t:0.5,d:11,s:0.2,g:1,}],\r\n [{w:\"sine\",v:0.4,a:0.1,d:11,},{w:\"sine\",v:6,f:2.5,d:0.05,s:1.1,g:1,}],\r\n [{w:\"sine\",v:0.3,d:0.1,r:0.1,},{w:\"square\",v:4,t:3,d:1,s:0.2,g:1,}],\r\n [{w:\"sine\",v:0.3,d:0.5,r:0.5,},{w:\"sine\",v:7,t:2,f:2,d:1,r:1,g:1,}],\r\n [{w:\"triangle\",v:0.6,h:0.03,d:0.3,r:0.3,t:0.5,},{w:\"n0\",v:8,t:1.5,d:0.08,r:0.08,g:1,}],\r\n /* 49-56 : Ensamble */\r\n [{w:\"sawtooth\",v:0.3,a:0.03,s:0.5,},{w:\"sawtooth\",v:0.2,t:2,f:2,d:1,s:2,}],\r\n [{w:\"sawtooth\",v:0.3,f:-2,a:0.03,s:0.5,},{w:\"sawtooth\",v:0.2,t:2,f:2,d:1,s:2,}],\r\n [{w:\"sawtooth\",v:0.2,a:0.02,s:1,},{w:\"sawtooth\",v:0.2,t:2,f:2,a:1,d:1,s:1,}],\r\n [{w:\"sawtooth\",v:0.2,a:0.02,s:1,},{w:\"sawtooth\",v:0.2,f:2,a:0.02,d:1,s:1,}],\r\n [{w:\"triangle\",v:0.3,a:0.03,s:1,},{w:\"sine\",v:3,t:5,f:1,d:1,s:1,g:1,}],\r\n [{w:\"sine\",v:0.4,a:0.03,s:0.9,},{w:\"sine\",v:1,t:2,f:3,d:0.03,s:0.2,g:1,}],\r\n [{w:\"triangle\",v:0.6,a:0.05,s:0.5,},{w:\"sine\",v:1,f:0.8,d:0.2,s:0.2,g:1,}],\r\n [{w:\"square\",v:0.15,a:0.01,d:0.2,r:0.2,t:0.5,h:0.03,},{w:\"square\",v:4,f:0.5,d:0.2,r:11,a:0.01,g:1,h:0.02,},{w:\"square\",v:0.15,t:4,f:1,a:0.02,d:0.15,r:0.15,h:0.03,},{g:3,w:\"square\",v:4,f:-0.5,a:0.01,h:0.02,d:0.15,r:11,}],\r\n /* 57-64 : Brass */\r\n [{w:\"square\",v:0.2,a:0.01,d:1,s:0.6,r:0.04,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.04,d:1,s:0.4,r:0.08,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.15,a:0.04,s:1,},{w:\"sine\",v:2,d:0.1,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.6,r:0.08,},{w:\"sine\",v:1,f:0.2,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:0.5,s:0.7,r:0.08,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:\"sine\",v:1,d:0.1,s:4,g:1,}],\r\n /* 65-72 : Reed */\r\n [{w:\"square\",v:0.2,a:0.02,d:2,s:0.6,},{w:\"sine\",v:2,d:1,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:2,s:0.6,},{w:\"sine\",v:2,d:1,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.6,},{w:\"sine\",v:2,d:1,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.02,d:1,s:0.6,},{w:\"sine\",v:2,d:1,g:1,}],\r\n [{w:\"sine\",v:0.4,a:0.02,d:0.7,s:0.5,},{w:\"square\",v:5,t:2,d:0.2,s:0.5,g:1,}],\r\n [{w:\"sine\",v:0.3,a:0.05,d:0.2,s:0.8,},{w:\"sawtooth\",v:6,f:0.1,d:0.1,s:0.3,g:1,}],\r\n [{w:\"sine\",v:0.3,a:0.03,d:0.2,s:0.4,},{w:\"square\",v:7,f:0.2,d:1,s:0.1,g:1,}],\r\n [{w:\"square\",v:0.2,a:0.05,d:0.1,s:0.8,},{w:\"square\",v:4,d:0.1,s:1.1,g:1,}],\r\n /* 73-80 : Pipe */\r\n [{w:\"sine\",a:0.02,d:2,},{w:\"sine\",v:6,t:2,d:0.04,g:1,}],\r\n [{w:\"sine\",v:0.7,a:0.03,d:0.4,s:0.4,},{w:\"sine\",v:4,t:2,f:0.2,d:0.4,g:1,}],\r\n [{w:\"sine\",v:0.7,a:0.02,d:0.4,s:0.6,},{w:\"sine\",v:3,t:2,d:0,s:1,g:1,}],\r\n [{w:\"sine\",v:0.4,a:0.06,d:0.3,s:0.3,},{w:\"sine\",v:7,t:2,d:0.2,s:0.2,g:1,}],\r\n [{w:\"sine\",a:0.02,d:0.3,s:0.3,},{w:\"sawtooth\",v:3,t:2,d:0.3,g:1,}],\r\n [{w:\"sine\",v:0.4,a:0.02,d:2,s:0.1,},{w:\"sawtooth\",v:8,t:2,f:1,d:0.5,g:1,}],\r\n [{w:\"sine\",v:0.7,a:0.03,d:0.5,s:0.3,},{w:\"sine\",v:0.003,t:0,f:4,d:0.1,s:0.002,g:1,}],\r\n [{w:\"sine\",v:0.7,a:0.02,d:2,},{w:\"sine\",v:1,t:2,f:1,d:0.02,g:1,}],\r\n /* 81-88 : SynthLead */\r\n [{w:\"square\",v:0.3,d:1,s:0.5,},{w:\"square\",v:1,f:0.2,d:1,s:0.5,g:1,}],\r\n [{w:\"sawtooth\",v:0.3,d:2,s:0.5,},{w:\"square\",v:2,f:0.1,s:0.5,g:1,}],\r\n [{w:\"triangle\",v:0.5,a:0.05,d:2,s:0.6,},{w:\"sine\",v:4,t:2,g:1,}],\r\n [{w:\"triangle\",v:0.3,a:0.01,d:2,s:0.3,},{w:\"sine\",v:22,t:2,f:1,d:0.03,s:0.2,g:1,}],\r\n [{w:\"sawtooth\",v:0.3,d:1,s:0.5,},{w:\"sine\",v:11,t:11,a:0.2,d:0.05,s:0.3,g:1,}],\r\n [{w:\"sine\",v:0.3,a:0.06,d:1,s:0.5,},{w:\"sine\",v:7,f:1,d:1,s:0.2,g:1,}],\r\n [{w:\"sawtooth\",v:0.3,a:0.03,d:0.7,s:0.3,r:0.2,},{w:\"sawtooth\",v:0.3,t:0.75,d:0.7,a:0.1,s:0.3,r:0.2,}],\r\n [{w:\"triangle\",v:0.3,a:0.01,d:0.7,s:0.5,},{w:\"square\",v:5,t:0.5,d:0.7,s:0.5,g:1,}],\r\n /* 89-96 : SynthPad */\r\n [{w:\"triangle\",v:0.3,a:0.02,d:0.3,s:0.3,r:0.3,},{w:\"square\",v:3,t:4,f:1,a:0.02,d:0.1,s:1,g:1,},{w:\"triangle\",v:0.08,t:0.5,a:0.1,h:0,d:0.1,s:0.5,r:0.1,b:0,c:0,}],\r\n [{w:\"sine\",v:0.3,a:0.05,d:1,s:0.7,r:0.3,},{w:\"sine\",v:2,f:1,d:0.3,s:1,g:1,}],\r\n [{w:\"square\",v:0.3,a:0.03,d:0.5,s:0.3,r:0.1,},{w:\"square\",v:4,f:1,a:0.03,d:0.1,g:1,}],\r\n [{w:\"triangle\",v:0.3,a:0.08,d:1,s:0.3,r:0.1,},{w:\"square\",v:2,f:1,d:0.3,s:0.3,g:1,t:4,a:0.08,}],\r\n [{w:\"sine\",v:0.3,a:0.05,d:1,s:0.3,r:0.1,},{w:\"sine\",v:0.1,t:2.001,f:1,d:1,s:50,g:1,}],\r\n [{w:\"triangle\",v:0.3,a:0.03,d:0.7,s:0.3,r:0.2,},{w:\"sine\",v:12,t:7,f:1,d:0.5,s:1.7,g:1,}],\r\n [{w:\"sine\",v:0.3,a:0.05,d:1,s:0.3,r:0.1,},{w:\"sawtooth\",v:22,t:6,d:0.06,s:0.3,g:1,}],\r\n [{w:\"triangle\",v:0.3,a:0.05,d:11,r:0.3,},{w:\"triangle\",v:1,d:1,s:8,g:1,}],\r\n /* 97-104 : FX */\r\n [{w:\"sawtooth\",v:0.3,d:4,s:0.8,r:0.1,},{w:\"square\",v:1,t:2,f:8,a:1,d:1,s:1,r:0.1,g:1,}],\r\n [{w:\"triangle\",v:0.3,d:1,s:0.5,t:0.8,a:0.2,p:1.25,q:0.2,},{w:\"sawtooth\",v:0.2,a:0.2,d:0.3,s:1,t:1.2,p:1.25,q:0.2,}],\r\n [{w:\"sine\",v:0.3,d:1,s:0.3,},{w:\"square\",v:22,t:11,d:0.5,s:0.1,g:1,}],\r\n [{w:\"sawtooth\",v:0.3,a:0.04,d:1,s:0.8,r:0.1,},{w:\"square\",v:1,t:0.5,d:1,s:2,g:1,}],\r\n [{w:\"triangle\",v:0.3,d:1,s:0.3,},{w:\"sine\",v:22,t:6,d:0.6,s:0.05,g:1,}],\r\n [{w:\"sine\",v:0.6,a:0.1,d:0.05,s:0.4,},{w:\"sine\",v:5,t:5,f:1,d:0.05,s:0.3,g:1,}],\r\n [{w:\"sine\",a:0.1,d:0.05,s:0.4,v:0.8,},{w:\"sine\",v:5,t:5,f:1,d:0.05,s:0.3,g:1,}],\r\n [{w:\"square\",v:0.3,a:0.1,d:0.1,s:0.4,},{w:\"square\",v:1,f:1,d:0.3,s:0.1,g:1,}],\r\n /* 105-112 : Ethnic */\r\n [{w:\"sawtooth\",v:0.3,d:0.5,r:0.5,},{w:\"sawtooth\",v:11,t:5,d:0.05,g:1,}],\r\n [{w:\"square\",v:0.3,d:0.2,r:0.2,},{w:\"square\",v:7,t:3,d:0.05,g:1,}],\r\n [{w:\"triangle\",d:0.2,r:0.2,},{w:\"square\",v:9,t:3,d:0.1,r:0.1,g:1,}],\r\n [{w:\"triangle\",d:0.3,r:0.3,},{w:\"square\",v:6,t:3,d:1,r:1,g:1,}],\r\n [{w:\"triangle\",v:0.4,d:0.2,r:0.2,},{w:\"square\",v:22,t:12,d:0.1,r:0.1,g:1,}],\r\n [{w:\"sine\",v:0.25,a:0.02,d:0.05,s:0.8,},{w:\"square\",v:1,t:2,d:0.03,s:11,g:1,}],\r\n [{w:\"sine\",v:0.3,a:0.05,d:11,},{w:\"square\",v:7,t:3,f:1,s:0.7,g:1,}],\r\n [{w:\"square\",v:0.3,a:0.05,d:0.1,s:0.8,},{w:\"square\",v:4,d:0.1,s:1.1,g:1,}],\r\n /* 113-120 : Percussive */\r\n [{w:\"sine\",v:0.4,d:0.3,r:0.3,},{w:\"sine\",v:7,t:9,d:0.1,r:0.1,g:1,}],\r\n [{w:\"sine\",v:0.7,d:0.1,r:0.1,},{w:\"sine\",v:22,t:7,d:0.05,g:1,}],\r\n [{w:\"sine\",v:0.6,d:0.15,r:0.15,},{w:\"square\",v:11,t:3.2,d:0.1,r:0.1,g:1,}],\r\n [{w:\"sine\",v:0.8,d:0.07,r:0.07,},{w:\"square\",v:11,t:7,r:0.01,g:1,}],\r\n [{w:\"triangle\",v:0.7,t:0.5,d:0.2,r:0.2,p:0.95,},{w:\"n0\",v:9,g:1,d:0.2,r:0.2,}],\r\n [{w:\"sine\",v:0.7,d:0.1,r:0.1,p:0.9,},{w:\"square\",v:14,t:2,d:0.005,r:0.005,g:1,}],\r\n [{w:\"square\",d:0.15,r:0.15,p:0.5,},{w:\"square\",v:4,t:5,d:0.001,r:0.001,g:1,}],\r\n [{w:\"n1\",v:0.3,a:1,s:1,d:0.15,r:0,t:0.5,}],\r\n /* 121-128 : SE */\r\n [{w:\"sine\",t:12.5,d:0,r:0,p:0.5,v:0.3,h:0.2,q:0.5,},{g:1,w:\"sine\",v:1,t:2,d:0,r:0,s:1,},{g:1,w:\"n0\",v:0.2,t:2,a:0.6,h:0,d:0.1,r:0.1,b:0,c:0,}],\r\n [{w:\"n0\",v:0.2,a:0.05,h:0.02,d:0.02,r:0.02,}],\r\n [{w:\"n0\",v:0.4,a:1,d:1,t:0.25,}],\r\n [{w:\"sine\",v:0.3,a:0.1,d:1,s:0.5,},{w:\"sine\",v:4,t:0,f:1.5,d:1,s:1,r:0.1,g:1,},{g:1,w:\"sine\",v:4,t:0,f:2,a:0.6,h:0,d:0.1,s:1,r:0.1,b:0,c:0,}],\r\n [{w:\"square\",v:0.3,t:0.25,d:11,s:1,},{w:\"square\",v:12,t:0,f:8,d:1,s:1,r:11,g:1,}],\r\n [{w:\"n0\",v:0.4,t:0.5,a:1,d:11,s:1,r:0.5,},{w:\"square\",v:1,t:0,f:14,d:1,s:1,r:11,g:1,}],\r\n [{w:\"sine\",t:0,f:1221,a:0.2,d:1,r:0.25,s:1,},{g:1,w:\"n0\",v:3,t:0.5,d:1,s:1,r:1,}],\r\n [{w:\"sine\",d:0.4,r:0.4,p:0.1,t:2.5,v:1,},{w:\"n0\",v:12,t:2,d:1,r:1,g:1,}],\r\n ],\r\n program0:[\r\n// 1-8 : Piano\r\n [{w:\"triangle\",v:.5,d:.7}], [{w:\"triangle\",v:.5,d:.7}],\r\n [{w:\"triangle\",v:.5,d:.7}], [{w:\"triangle\",v:.5,d:.7}],\r\n [{w:\"triangle\",v:.5,d:.7}], [{w:\"triangle\",v:.5,d:.7}],\r\n [{w:\"sawtooth\",v:.3,d:.7}], [{w:\"sawtooth\",v:.3,d:.7}],\r\n/* 9-16 : Chromatic Perc*/\r\n [{w:\"sine\",v:.5,d:.3,r:.3}], [{w:\"triangle\",v:.5,d:.3,r:.3}],\r\n [{w:\"square\",v:.2,d:.3,r:.3}], [{w:\"square\",v:.2,d:.3,r:.3}],\r\n [{w:\"sine\",v:.5,d:.1,r:.1}], [{w:\"sine\",v:.5,d:.1,r:.1}],\r\n [{w:\"square\",v:.2,d:1,r:1}], [{w:\"sawtooth\",v:.3,d:.7,r:.7}],\r\n/* 17-24 : Organ */\r\n [{w:\"sine\",v:0.5,a:0.01,s:1}], [{w:\"sine\",v:0.7,d:0.02,s:0.7}],\r\n [{w:\"square\",v:.2,s:1}], [{w:\"triangle\",v:.5,a:.01,s:1}],\r\n [{w:\"square\",v:.2,a:.02,s:1}], [{w:\"square\",v:0.2,a:0.02,s:1}],\r\n [{w:\"square\",v:0.2,a:0.02,s:1}], [{w:\"square\",v:.2,a:.05,s:1}],\r\n/* 25-32 : Guitar */\r\n [{w:\"triangle\",v:.5,d:.5}], [{w:\"square\",v:.2,d:.6}],\r\n [{w:\"square\",v:.2,d:.6}], [{w:\"triangle\",v:.8,d:.6}],\r\n [{w:\"triangle\",v:.4,d:.05}], [{w:\"square\",v:.2,d:1}],\r\n [{w:\"square\",v:.2,d:1}], [{w:\"sine\",v:.4,d:.6}],\r\n/* 33-40 : Bass */\r\n [{w:\"triangle\",v:.7,d:.4}], [{w:\"triangle\",v:.7,d:.7}],\r\n [{w:\"triangle\",v:.7,d:.7}], [{w:\"triangle\",v:.7,d:.7}],\r\n [{w:\"square\",v:.3,d:.2}], [{w:\"square\",v:.3,d:.2}],\r\n [{w:\"square\",v:.3,d:.1,s:.2}], [{w:\"sawtooth\",v:.4,d:.1,s:.2}],\r\n/* 41-48 : Strings */\r\n [{w:\"sawtooth\",v:.2,a:.02,s:1}], [{w:\"sawtooth\",v:.2,a:.02,s:1}],\r\n [{w:\"sawtooth\",v:.2,a:.02,s:1}], [{w:\"sawtooth\",v:.2,a:.02,s:1}],\r\n [{w:\"sawtooth\",v:.2,a:.02,s:1}], [{w:\"sawtooth\",v:.3,d:.1}],\r\n [{w:\"sawtooth\",v:.3,d:.5,r:.5}], [{w:\"triangle\",v:.6,d:.1,r:.1,h:0.03,p:0.8}],\r\n/* 49-56 : Ensamble */\r\n [{w:\"sawtooth\",v:.2,a:.02,s:1}], [{w:\"sawtooth\",v:.2,a:.02,s:1}],\r\n [{w:\"sawtooth\",v:.2,a:.02,s:1}], [{w:\"sawtooth\",v:.2,a:.02,s:1}],\r\n [{w:\"triangle\",v:.3,a:.03,s:1}], [{w:\"sine\",v:.3,a:.03,s:1}],\r\n [{w:\"triangle\",v:.3,a:.05,s:1}], [{w:\"sawtooth\",v:.5,a:.01,d:.1}],\r\n/* 57-64 : Brass */\r\n [{w:\"square\",v:.3,a:.05,d:.2,s:.6}], [{w:\"square\",v:.3,a:.05,d:.2,s:.6}],\r\n [{w:\"square\",v:.3,a:.05,d:.2,s:.6}], [{w:\"square\",v:0.2,a:.05,d:0.01,s:1}],\r\n [{w:\"square\",v:.3,a:.05,s:1}], [{w:\"square\",v:.3,s:.7}],\r\n [{w:\"square\",v:.3,s:.7}], [{w:\"square\",v:.3,s:.7}],\r\n/* 65-72 : Reed */\r\n [{w:\"square\",v:.3,a:.02,d:2}], [{w:\"square\",v:.3,a:.02,d:2}],\r\n [{w:\"square\",v:.3,a:.03,d:2}], [{w:\"square\",v:.3,a:.04,d:2}],\r\n [{w:\"square\",v:.3,a:.02,d:2}], [{w:\"square\",v:.3,a:.05,d:2}],\r\n [{w:\"square\",v:.3,a:.03,d:2}], [{w:\"square\",v:.3,a:.03,d:2}],\r\n/* 73-80 : Pipe */\r\n [{w:\"sine\",v:.7,a:.02,d:2}], [{w:\"sine\",v:.7,a:.02,d:2}],\r\n [{w:\"sine\",v:.7,a:.02,d:2}], [{w:\"sine\",v:.7,a:.02,d:2}],\r\n [{w:\"sine\",v:.7,a:.02,d:2}], [{w:\"sine\",v:.7,a:.02,d:2}],\r\n [{w:\"sine\",v:.7,a:.02,d:2}], [{w:\"sine\",v:.7,a:.02,d:2}],\r\n/* 81-88 : SynthLead */\r\n [{w:\"square\",v:.3,s:.7}], [{w:\"sawtooth\",v:.4,s:.7}],\r\n [{w:\"triangle\",v:.5,s:.7}], [{w:\"sawtooth\",v:.4,s:.7}],\r\n [{w:\"sawtooth\",v:.4,d:12}], [{w:\"sine\",v:.4,a:.06,d:12}],\r\n [{w:\"sawtooth\",v:.4,d:12}], [{w:\"sawtooth\",v:.4,d:12}],\r\n/* 89-96 : SynthPad */\r\n [{w:\"sawtooth\",v:.3,d:12}], [{w:\"triangle\",v:.5,d:12}],\r\n [{w:\"square\",v:.3,d:12}], [{w:\"triangle\",v:.5,a:.08,d:11}],\r\n [{w:\"sawtooth\",v:.5,a:.05,d:11}], [{w:\"sawtooth\",v:.5,d:11}],\r\n [{w:\"triangle\",v:.5,d:11}], [{w:\"triangle\",v:.5,d:11}],\r\n/* 97-104 : FX */\r\n [{w:\"triangle\",v:.5,d:11}], [{w:\"triangle\",v:.5,d:11}],\r\n [{w:\"square\",v:.3,d:11}], [{w:\"sawtooth\",v:0.5,a:0.04,d:11}],\r\n [{w:\"sawtooth\",v:.5,d:11}], [{w:\"triangle\",v:.5,a:.8,d:11}],\r\n [{w:\"triangle\",v:.5,d:11}], [{w:\"square\",v:.3,d:11}],\r\n/* 105-112 : Ethnic */\r\n [{w:\"sawtooth\",v:.3,d:1,r:1}], [{w:\"sawtooth\",v:.5,d:.3}],\r\n [{w:\"sawtooth\",v:.5,d:.3,r:.3}], [{w:\"sawtooth\",v:.5,d:.3,r:.3}],\r\n [{w:\"square\",v:.3,d:.2,r:.2}], [{w:\"square\",v:.3,a:.02,d:2}],\r\n [{w:\"sawtooth\",v:.2,a:.02,d:.7}], [{w:\"triangle\",v:.5,d:1}],\r\n/* 113-120 : Percussive */\r\n [{w:\"sawtooth\",v:.3,d:.3,r:.3}], [{w:\"sine\",v:.8,d:.1,r:.1}],\r\n [{w:\"square\",v:.2,d:.1,r:.1,p:1.05}], [{w:\"sine\",v:.8,d:.05,r:.05}],\r\n [{w:\"triangle\",v:0.5,d:0.1,r:0.1,p:0.96}], [{w:\"triangle\",v:0.5,d:0.1,r:0.1,p:0.97}],\r\n [{w:\"square\",v:.3,d:.1,r:.1,}], [{w:\"n1\",v:0.3,a:1,s:1,d:0.15,r:0,t:0.5,}],\r\n/* 121-128 : SE */\r\n [{w:\"triangle\",v:0.5,d:0.03,t:0,f:1332,r:0.001,p:1.1}],\r\n [{w:\"n0\",v:0.2,t:0.1,d:0.02,a:0.05,h:0.02,r:0.02}],\r\n [{w:\"n0\",v:0.4,a:1,d:1,t:0.25,}],\r\n [{w:\"sine\",v:0.3,a:0.8,d:1,t:0,f:1832}],\r\n [{w:\"triangle\",d:0.5,t:0,f:444,s:1,}],\r\n [{w:\"n0\",v:0.4,d:1,t:0,f:22,s:1,}],\r\n [{w:\"n0\",v:0.5,a:0.2,d:11,t:0,f:44}],\r\n [{w:\"n0\",v:0.5,t:0.25,d:0.4,r:0.4}],\r\n ],\r\n drummap1:[\r\n/*35*/ [{w:\"triangle\",t:0,f:70,v:1,d:0.05,h:0.03,p:0.9,q:0.1,},{w:\"n0\",g:1,t:6,v:17,r:0.01,h:0,p:0,}],\r\n [{w:\"triangle\",t:0,f:88,v:1,d:0.05,h:0.03,p:0.5,q:0.1,},{w:\"n0\",g:1,t:5,v:42,r:0.01,h:0,p:0,}],\r\n [{w:\"n0\",f:222,p:0,t:0,r:0.01,h:0,}],\r\n [{w:\"triangle\",v:0.3,f:180,d:0.05,t:0,h:0.03,p:0.9,q:0.1,},{w:\"n0\",v:0.6,t:0,f:70,h:0.02,r:0.01,p:0,},{g:1,w:\"square\",v:2,t:0,f:360,r:0.01,b:0,c:0,}],\r\n [{w:\"square\",f:1150,v:0.34,t:0,r:0.03,h:0.025,d:0.03,},{g:1,w:\"n0\",t:0,f:13,h:0.025,d:0.1,s:1,r:0.1,v:1,}],\r\n/*40*/ [{w:\"triangle\",f:200,v:1,d:0.06,t:0,r:0.06,},{w:\"n0\",g:1,t:0,f:400,v:12,r:0.02,d:0.02,}],\r\n [{w:\"triangle\",f:100,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.4,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"n1\",f:390,v:0.25,r:0.01,t:0,}],\r\n [{w:\"triangle\",f:120,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.5,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"n1\",v:0.25,f:390,r:0.03,t:0,h:0.005,d:0.03,}],\r\n/*45*/ [{w:\"triangle\",f:140,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"n1\",v:0.25,f:390,t:0,d:0.2,r:0.2,},{w:\"n0\",v:0.3,t:0,c:0,f:440,h:0.005,d:0.05,}],\r\n [{w:\"triangle\",f:155,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"triangle\",f:180,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"n1\",v:0.3,f:1200,d:0.2,r:0.2,h:0.05,t:0,},{w:\"n1\",t:0,v:1,d:0.1,r:0.1,p:1.2,f:440,}],\r\n/*50*/ [{w:\"triangle\",f:220,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:\"n0\",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],\r\n [{w:\"n1\",f:500,v:0.15,d:0.4,r:0.4,h:0,t:0,},{w:\"n0\",v:0.1,t:0,r:0.01,f:440,}],\r\n [{w:\"n1\",v:0.3,f:800,d:0.2,r:0.2,h:0.05,t:0,},{w:\"square\",t:0,v:1,d:0.1,r:0.1,p:0.1,f:220,g:1,}],\r\n [{w:\"sine\",f:1651,v:0.15,d:0.2,r:0.2,h:0,t:0,},{w:\"sawtooth\",g:1,t:1.21,v:7.2,d:0.1,r:11,h:1,},{g:1,w:\"n0\",v:3.1,t:0.152,d:0.002,r:0.002,}],\r\n null,\r\n/*55*/ [{w:\"n1\",v:.3,f:1200,d:0.2,r:0.2,h:0.05,t:0,},{w:\"n1\",t:0,v:1,d:0.1,r:0.1,p:1.2,f:440,}],\r\n null,\r\n [{w:\"n1\",v:0.3,f:555,d:0.25,r:0.25,h:0.05,t:0,},{w:\"n1\",t:0,v:1,d:0.1,r:0.1,f:440,a:0.005,h:0.02,}],\r\n [{w:\"sawtooth\",f:776,v:0.2,d:0.3,t:0,r:0.3,},{g:1,w:\"n0\",v:2,t:0,f:776,a:0.005,h:0.02,d:0.1,s:1,r:0.1,c:0,},{g:11,w:\"sine\",v:0.1,t:0,f:22,d:0.3,r:0.3,b:0,c:0,}],\r\n [{w:\"n1\",f:440,v:0.15,d:0.4,r:0.4,h:0,t:0,},{w:\"n0\",v:0.4,t:0,r:0.01,f:440,}],\r\n/*60*/ null,null,null,null,null,\r\n/*65*/ null,null,null,null,null,\r\n/*70*/ null,null,null,null,null,\r\n/*75*/ null,null,null,null,null,\r\n/*80*/ [{w:\"sine\",f:1720,v:0.3,d:0.02,t:0,r:0.02,},{w:\"square\",g:1,t:0,f:2876,v:6,d:0.2,s:1,r:0.2,}],\r\n [{w:\"sine\",f:1720,v:0.3,d:0.25,t:0,r:0.25,},{w:\"square\",g:1,t:0,f:2876,v:6,d:0.2,s:1,r:0.2,}],\r\n ],\r\n drummap0:[\r\n/*35*/[{w:\"triangle\",t:0,f:110,v:1,d:0.05,h:0.02,p:0.1,}],\r\n [{w:\"triangle\",t:0,f:150,v:0.8,d:0.1,p:0.1,h:0.02,r:0.01,}],\r\n [{w:\"n0\",f:392,v:0.5,d:0.01,p:0,t:0,r:0.05}],\r\n [{w:\"n0\",f:33,d:0.05,t:0,}],\r\n [{w:\"n0\",f:100,v:0.7,d:0.03,t:0,r:0.03,h:0.02,}],\r\n/*40*/[{w:\"n0\",f:44,v:0.7,d:0.02,p:0.1,t:0,h:0.02,}],\r\n [{w:\"triangle\",f:240,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"n0\",f:440,v:0.2,r:0.01,t:0,}],\r\n [{w:\"triangle\",f:270,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"n0\",f:440,v:0.2,d:0.04,r:0.04,t:0,}],\r\n/*45*/[{w:\"triangle\",f:300,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"n0\",f:440,v:0.2,d:0.1,r:0.1,h:0.02,t:0,}],\r\n [{w:\"triangle\",f:320,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"triangle\",f:360,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"n0\",f:150,v:0.2,d:0.1,r:0.1,h:0.05,t:0,p:0.1,}],\r\n/*50*/[{w:\"triangle\",f:400,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],\r\n [{w:\"n0\",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],\r\n [{w:\"n0\",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],\r\n [{w:\"n0\",f:440,v:0.3,d:0.1,p:0.9,t:0,r:0.1,}],\r\n [{w:\"n0\",f:200,v:0.2,d:0.05,p:0.9,t:0,}],\r\n/*55*/[{w:\"n0\",f:440,v:0.3,d:0.12,p:0.9,t:0,}],\r\n [{w:\"sine\",f:800,v:0.4,d:0.06,t:0,}],\r\n [{w:\"n0\",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],\r\n [{w:\"n0\",f:33,v:0.3,d:0.2,p:0.9,t:0,}],\r\n [{w:\"n0\",f:300,v:0.3,d:0.14,p:0.9,t:0,}],\r\n/*60*/[{w:\"sine\",f:200,d:0.06,t:0,}],\r\n [{w:\"sine\",f:150,d:0.06,t:0,}],\r\n [{w:\"sine\",f:300,t:0,}],\r\n [{w:\"sine\",f:300,d:0.06,t:0,}],\r\n [{w:\"sine\",f:250,d:0.06,t:0,}],\r\n/*65*/[{w:\"square\",f:300,v:.3,d:.06,p:.8,t:0,}],\r\n [{w:\"square\",f:260,v:.3,d:.06,p:.8,t:0,}],\r\n [{w:\"sine\",f:850,v:.5,d:.07,t:0,}],\r\n [{w:\"sine\",f:790,v:.5,d:.07,t:0,}],\r\n [{w:\"n0\",f:440,v:0.3,a:0.05,t:0,}],\r\n/*70*/[{w:\"n0\",f:440,v:0.3,a:0.05,t:0,}],\r\n [{w:\"triangle\",f:1800,v:0.4,p:0.9,t:0,h:0.03,}],\r\n [{w:\"triangle\",f:1800,v:0.3,p:0.9,t:0,h:0.13,}],\r\n [{w:\"n0\",f:330,v:0.3,a:0.02,t:0,r:0.01,}],\r\n [{w:\"n0\",f:330,v:0.3,a:0.02,t:0,h:0.04,r:0.01,}],\r\n/*75*/[{w:\"n0\",f:440,v:0.3,t:0,}],\r\n [{w:\"sine\",f:800,t:0,}],\r\n [{w:\"sine\",f:700,t:0,}],\r\n [{w:\"n0\",f:330,v:0.3,t:0,}],\r\n [{w:\"n0\",f:330,v:0.3,t:0,h:0.1,r:0.01,p:0.7,}],\r\n/*80*/[{w:\"sine\",t:0,f:1200,v:0.3,r:0.01,}],\r\n [{w:\"sine\",t:0,f:1200,v:0.3,d:0.2,r:0.2,}],\r\n\r\n ],\r\n /*@@gui*/\r\n _guiInit:()=>{\r\n if(this.canvas){\r\n this.ctx=this.canvas.getContext(\"2d\");\r\n this.ctx.fillStyle=\"#000\";\r\n this.ctx.fillRect(0,0,300,32);\r\n this.canvas.addEventListener(\"dragover\",this.dragOver.bind(this),false);\r\n this.canvas.addEventListener(\"dragleave\",this.dragLeave.bind(this),false);\r\n this.canvas.addEventListener(\"drop\",this.execDrop.bind(this),false);\r\n this.canvas.addEventListener(\"click\",this.click.bind(this),false);\r\n this.canvas.addEventListener(\"mousedown\",this.pointerdown.bind(this),false);\r\n this.canvas.addEventListener(\"mousemove\",this.pointermove.bind(this),false);\r\n this.canvas.addEventListener(\"touchstart\",this.pointerdown.bind(this),false);\r\n this.canvas.addEventListener(\"touchend\",this.pointerup.bind(this),false);\r\n this.canvas.addEventListener(\"touchcancel\",this.pointerup.bind(this),false);\r\n this.canvas.addEventListener(\"touchmove\",this.pointermove.bind(this),false);\r\n }\r\n },\r\n _guiUpdate:()=>{\r\n if(this.canvas){\r\n this.ctx.fillStyle=\"#000\";\r\n this.ctx.fillRect(0,0,300,32);\r\n var row1=8,row2=20;\r\n if(this.song)\r\n row1=4,row2=24;\r\n else {\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.fillText(\"TinySynth\",8,20);\r\n }\r\n if(this.graph){\r\n this.ctx.fillStyle=\"#800\";\r\n this.ctx.fillRect(80,row1,132,4);\r\n this.ctx.fillRect(80,row2,132,4);\r\n this.ctx.fillStyle=\"#f00\";\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n const nt=this.notetab[i];\r\n if(!nt.f || this.rhythm[nt.ch]){\r\n this.ctx.fillRect(80+nt.n,row1,4,4);\r\n this.ctx.fillRect(80+nt.ch*8,row2,6,4);\r\n }\r\n }\r\n }\r\n if(this.perfmon){\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.fillRect(180,30,28,-12);\r\n this.ctx.fillStyle=\"#000\";\r\n this.ctx.fillText(this.notetab.length,185,28);\r\n }\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.fillRect(250,15,32,2);\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.strokeStyle=\"#000\";\r\n this.ctx.beginPath();\r\n this.ctx.arc(250+this.masterVol*32,16,6,0,6.28,0);\r\n this.ctx.moveTo(220,12); this.ctx.lineTo(224,12); this.ctx.lineTo(230,6);\r\n this.ctx.lineTo(230,26); this.ctx.lineTo(224,20); this.ctx.lineTo(220,20);\r\n this.ctx.fill();\r\n this.ctx.stroke();\r\n this.ctx.strokeStyle=\"#fff\";\r\n this.ctx.lineWidth=2;\r\n this.ctx.beginPath();\r\n this.ctx.arc(230,16,4,-1,1,false);\r\n this.ctx.stroke();\r\n this.ctx.beginPath();\r\n this.ctx.arc(230,16,8,-1,1,false);\r\n this.ctx.stroke();\r\n if(this.masterVol==0){\r\n this.ctx.strokeStyle=\"#000\";\r\n this.ctx.lineWidth=4;\r\n this.ctx.beginPath();\r\n this.ctx.moveTo(220,7);\r\n this.ctx.lineTo(238,25);\r\n this.ctx.stroke();\r\n this.ctx.strokeStyle=\"#fff\";\r\n this.ctx.lineWidth=2;\r\n this.ctx.stroke();\r\n }\r\n if(this.song){\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.fillRect(4,2,28,28);\r\n this.ctx.fillRect(80,15,128,2);\r\n this.ctx.fillStyle=\"#000\";\r\n if(this.playing){\r\n this.ctx.fillRect(12,10,4,12);\r\n this.ctx.fillRect(22,10,4,12);\r\n }\r\n else{\r\n this.ctx.beginPath();\r\n this.ctx.moveTo(12,9);\r\n this.ctx.lineTo(25,16);\r\n this.ctx.lineTo(12,23);\r\n this.ctx.fill();\r\n }\r\n this.ctx.fillStyle=\"#fff\"\r\n this.ctx.fillText(this.toTime(this.playTick),38,14);\r\n this.ctx.fillText(this.toTime(this.maxTick),38,28);\r\n this.ctx.strokeStyle=\"#000\";\r\n this.ctx.beginPath();\r\n this.ctx.arc(80+this.playTick/this.maxTick*128,16,6,0,6.28,0);\r\n this.ctx.fill();\r\n this.ctx.stroke();\r\n }\r\n if(this.waitdrop){\r\n this.ctx.fillStyle=\"rgba(0,0,0,0.7)\"\r\n this.ctx.fillRect(0,0,300,32);\r\n this.ctx.fillStyle=\"#fff\";\r\n this.ctx.fillText(\"Drop MIDI File Here\",100,20);\r\n }\r\n }\r\n },\r\n toTime:(ti)=>{\r\n ti=(ti*4*60/this.song.timebase/this.song.tempo)|0;\r\n const m=(ti/60)|0;\r\n const s=ti%60;\r\n return (\"00\"+m).substr(-2)+\":\"+(\"00\"+s).substr(-2);\r\n },\r\n preventScroll:(e)=>{\r\n e.preventDefault();\r\n },\r\n pointerup:(ev)=>{\r\n document.body.removeEventListener('touchstart',this.preventScroll,false);\r\n },\r\n getPos:(e)=>{\r\n var p=e.target.getBoundingClientRect();\r\n if(p.right!=p.left)\r\n return {x:(e.clientX-p.left)*300/(p.right-p.left),y:e.clientY-p.top};\r\n return {x:0,y:0};\r\n },\r\n pointerdown:(ev)=>{\r\n let e=ev;\r\n if(ev.touches)\r\n e=ev.touches[0];\r\n this.downpos=this.getPos(e);\r\n if(ev.touches || (e.buttons&1)){\r\n if(this.song&&this.downpos.x>=80&&this.downpos.x<=208){\r\n const p=(this.downpos.x-80)/128*this.maxTick;\r\n this.locateMIDI(p);\r\n document.body.addEventListener('touchstart',this.preventScroll,false);\r\n }\r\n if(this.downpos.x>=250&&this.downpos.x<282){\r\n const p=(this.downpos.x-250)/32;\r\n this.setMasterVol(p);\r\n document.body.addEventListener('touchstart',this.preventScroll,false);\r\n }\r\n }\r\n },\r\n pointermove:(ev)=>{\r\n let e=ev;\r\n if(ev.touches)\r\n e=ev.touches[0];\r\n if(ev.touches || (e.buttons&1)){\r\n const pos=this.getPos(e);\r\n if(this.song&&pos.x>=70&&pos.x<=208){\r\n if(pos.x<80) pos.x=80;\r\n const p=(pos.x-80)/128*this.maxTick;\r\n this.locateMIDI(p);\r\n }\r\n if(pos.x>=250&&pos.x<282){\r\n const p=(pos.x-250)/32;\r\n this.setMasterVol(p);\r\n }\r\n }\r\n },\r\n click:(e)=>{\r\n const pos=this.getPos(e);\r\n if(pos.x<40 && this.song){\r\n if(this.playing)\r\n this.stopMIDI();\r\n else if(this.song)\r\n this.playMIDI();\r\n }\r\n if(pos.x>=215&&pos.x<243 && this.downpos.x>=215 && this.downpos.x<243){\r\n if(this.masterVol>0){\r\n this.lastMasterVol=this.masterVol;\r\n this.masterVol=0;\r\n }\r\n else\r\n this.masterVol=this.lastMasterVol;\r\n }\r\n },\r\n dragLeave:(e)=>{\r\n this.waitdrop=0;\r\n },\r\n dragOver:(e)=>{\r\n this.waitdrop=1;\r\n e.stopPropagation();\r\n e.preventDefault();\r\n e.dataTransfer.dropEffect = \"copy\";\r\n },\r\n execDrop:(e)=>{\r\n this.waitdrop=0;\r\n const f = e.dataTransfer.files;\r\n if(this.disabledrop==0){\r\n var reader = new FileReader();\r\n reader.onload=function(e){\r\n this.loadMIDI(reader.result);\r\n }.bind(this);\r\n reader.readAsArrayBuffer(f[0]);\r\n }\r\n e.stopPropagation();\r\n e.preventDefault();\r\n },\r\n /*@@guiEND*/\r\n ready:()=>{\r\n return new Promise((resolv)=>{\r\n const timerid=setInterval(()=>{\r\n// console.log(\"Initialize checking.\");\r\n if(this.isReady){\r\n clearInterval(timerid);\r\n console.log(\"Initialized\");\r\n resolv();\r\n }\r\n },100);\r\n });\r\n },\r\n init:()=>{\r\n this.pg=[]; this.vol=[]; this.ex=[]; this.bend=[]; this.rpnidx=[]; this.brange=[];\r\n this.sustain=[]; this.notetab=[]; this.rhythm=[];\r\n this.masterTuningC=0; this.masterTuningF=0; this.tuningC=[]; this.tuningF=[];\r\n this.maxTick=0, this.playTick=0, this.playing=0; this.releaseRatio=3.5;\r\n for(let i=0;i<16;++i){\r\n this.pg[i]=0; this.vol[i]=3*100*100/(127*127);\r\n this.bend[i]=0; this.brange[i]=0x100;\r\n this.tuningC[i]=0; this.tuningF[i]=0;\r\n this.rhythm[i]=0;\r\n }\r\n this.rhythm[9]=1;\r\n this.preroll=0.2;\r\n this.relcnt=0;\r\n setInterval(\r\n function(){\r\n if(++this.relcnt>=3){\r\n this.relcnt=0;\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n var nt=this.notetab[i];\r\n if(this.actx.currentTime>nt.e){\r\n this._pruneNote(nt);\r\n this.notetab.splice(i,1);\r\n }\r\n }\r\n /*@@gui*/\r\n /*@@guiEND*/\r\n }\r\n if(this.playing && this.song.ev.length>0){\r\n let e=this.song.ev[this.playIndex];\r\n while(this.actx.currentTime+this.preroll>this.playTime){\r\n if(e.m[0]==0xff51){\r\n this.song.tempo=e.m[1];\r\n this.tick2Time=4*60/this.song.tempo/this.song.timebase;\r\n }\r\n else\r\n this.send(e.m,this.playTime);\r\n ++this.playIndex;\r\n if(this.playIndex>=this.song.ev.length){\r\n if(this.loop){\r\n e=this.song.ev[this.playIndex=0];\r\n this.playTick=e.t;\r\n }\r\n else{\r\n this.playTick=this.maxTick;\r\n this.playing=0;\r\n break;\r\n }\r\n }\r\n else{\r\n e=this.song.ev[this.playIndex];\r\n this.playTime+=(e.t-this.playTick)*this.tick2Time;\r\n this.playTick=e.t;\r\n }\r\n }\r\n }\r\n }.bind(this),60\r\n );\r\n console.log(\"internalcontext:\"+this.internalcontext)\r\n if(this.internalcontext){\r\n window.AudioContext = window.AudioContext || window.webkitAudioContext;\r\n this.setAudioContext(new AudioContext());\r\n }\r\n this.isReady=1;\r\n },\r\n setMasterVol:(v)=>{\r\n if(v!=undefined)\r\n this.masterVol=v;\r\n if(this.out)\r\n this.out.gain.value=this.masterVol;\r\n },\r\n setReverbLev:(v)=>{\r\n if(v!=undefined)\r\n this.reverbLev=v;\r\n var r=parseFloat(this.reverbLev);\r\n if(this.rev&&!isNaN(r))\r\n this.rev.gain.value=r*8;\r\n },\r\n setLoop:(f)=>{\r\n this.loop=f;\r\n },\r\n setVoices:(v)=>{\r\n this.voices=v;\r\n },\r\n getPlayStatus:()=>{\r\n return {play:this.playing, maxTick:this.maxTick, curTick:this.playTick};\r\n },\r\n locateMIDI:(tick)=>{\r\n let i,p=this.playing;\r\n this.stopMIDI();\r\n for(i=0;ithis.song.ev[i].t;++i){\r\n var m=this.song.ev[i];\r\n var ch=m.m[0]&0xf;\r\n switch(m.m[0]&0xf0){\r\n case 0xb0:\r\n switch(m.m[1]){\r\n case 1: this.setModulation(ch,m.m[2]); break;\r\n case 7: this.setChVol(ch,m.m[2]); break;\r\n case 10: this.setPan(ch,m.m[2]); break;\r\n case 11: this.setExpression(ch,m.m[2]); break;\r\n case 64: this.setSustain(ch,m.m[2]); break;\r\n }\r\n break;\r\n case 0xc0: this.pg[m.m[0]&0x0f]=m.m[1]; break;\r\n }\r\n if(m.m[0]==0xff51)\r\n this.song.tempo=m.m[1];\r\n }\r\n if(!this.song.ev[i]){\r\n this.playIndex=0;\r\n this.playTick=this.maxTick;\r\n }\r\n else{\r\n this.playIndex=i;\r\n this.playTick=this.song.ev[i].t;\r\n }\r\n if(p)\r\n this.playMIDI();\r\n },\r\n getTimbreName:(m,n)=>{\r\n if(m==0)\r\n return this.program[n].name;\r\n else\r\n return this.drummap[n-35].name;\r\n },\r\n loadMIDIfromSrc:()=>{\r\n this.loadMIDIUrl(this.src);\r\n },\r\n loadMIDIUrl:(url)=>{\r\n if(!url)\r\n return;\r\n var xhr=new XMLHttpRequest();\r\n xhr.open(\"GET\",url,true);\r\n xhr.responseType=\"arraybuffer\";\r\n xhr.loadMIDI=this.loadMIDI.bind(this);\r\n xhr.onload=function(e){\r\n if(this.status==200){\r\n this.loadMIDI(this.response);\r\n }\r\n };\r\n xhr.send();\r\n },\r\n reset:()=>{\r\n for(let i=0;i<16;++i){\r\n this.setProgram(i,0);\r\n this.setBendRange(i,0x100);\r\n this.setModulation(i,0);\r\n this.setChVol(i,100);\r\n this.setPan(i,64);\r\n this.resetAllControllers(i);\r\n this.allSoundOff(i);\r\n this.rhythm[i]=0;\r\n this.tuningC[i]=0;\r\n this.tuningF[i]=0;\r\n }\r\n this.masterTuningC=0;\r\n this.masterTuningF=0;\r\n this.rhythm[9]=1;\r\n },\r\n stopMIDI:()=>{\r\n this.playing=0;\r\n for(var i=0;i<16;++i)\r\n this.allSoundOff(i);\r\n },\r\n playMIDI:()=>{\r\n if(!this.song)\r\n return;\r\n const dummy=this.actx.createOscillator();\r\n dummy.connect(this.actx.destination);\r\n dummy.frequency.value=0;\r\n dummy.start(0);\r\n dummy.stop(this.actx.currentTime+0.001);\r\n if(this.playTick>=this.maxTick)\r\n this.playTick=0,this.playIndex=0;\r\n this.playTime=this.actx.currentTime+.1;\r\n this.tick2Time=4*60/this.song.tempo/this.song.timebase;\r\n this.playing=1;\r\n },\r\n loadMIDI:(data)=>{\r\n function Get2(s, i) { return (s[i]<<8) + s[i+1]; }\r\n function Get3(s, i) { return (s[i]<<16) + (s[i+1]<<8) + s[i+2]; }\r\n function Get4(s, i) { return (s[i]<<24) + (s[i+1]<<16) + (s[i+2]<<8) + s[i+3]; }\r\n function GetStr(s, i, len) {\r\n return String.fromCharCode.apply(null,s.slice(i,i+len));\r\n }\r\n function Delta(s, i) {\r\n var v, d;\r\n v = 0;\r\n datalen = 1;\r\n while((d = s[i]) & 0x80) {\r\n v = (v<<7) + (d&0x7f);\r\n ++datalen;\r\n ++i;\r\n }\r\n return (v<<7)+d;\r\n }\r\n function Msg(song,tick,s,i){\r\n var v=s[i];\r\n datalen=1;\r\n if((v&0x80)==0)\r\n v=runst,datalen=0;\r\n runst=v;\r\n switch(v&0xf0){\r\n case 0xc0: case 0xd0:\r\n song.ev.push({t:tick,m:[v,s[i+datalen]]});\r\n datalen+=1;\r\n break;\r\n case 0xf0:\r\n switch(v) {\r\n case 0xf0:\r\n case 0xf7:\r\n var len=Delta(s,i+1);\r\n datastart=1+datalen;\r\n var exd=Array.from(s.slice(i+datastart,i+datastart+len));\r\n exd.unshift(0xf0);\r\n song.ev.push({t:tick,m:exd});\r\n/*\r\n var sysex=[];\r\n for(var jj=0;jjthis.maxTick)\r\n this.maxTick=tick;\r\n }\r\n idx += (len+8);\r\n }\r\n this.song.ev.sort(function(x,y){return x.t-y.t});\r\n this.reset();\r\n this.locateMIDI(0);\r\n },\r\n setQuality:(q)=>{\r\n if(q!=undefined)\r\n this.quality=q;\r\n for(let i=0;i<128;++i)\r\n this.setTimbre(0,i,this.program0[i]);\r\n for(let i=0;i{\r\n const defp={g:0,w:\"sine\",t:1,f:0,v:0.5,a:0,h:0.01,d:0.01,s:0,r:0.05,p:1,q:1,k:0};\r\n function filldef(p){\r\n for(n=0;n=35 && n<=81)\r\n this.drummap[n-35].p=filldef(p);\r\n if(m==0 && n>=0 && n<=127)\r\n this.program[n].p=filldef(p);\r\n },\r\n _pruneNote:(nt)=>{\r\n for(let k=nt.o.length-1;k>=0;--k){\r\n if(nt.o[k].frequency){\r\n nt.o[k].frequency.cancelScheduledValues(0);\r\n }\r\n else{\r\n nt.o[k].playbackRate.cancelScheduledValues(0);\r\n }\r\n nt.g[k].gain.cancelScheduledValues(0);\r\n\r\n nt.o[k].stop();\r\n if(nt.o[k].detune) {\r\n try {\r\n this.chmod[nt.ch].disconnect(nt.o[k].detune);\r\n } catch (e) {}\r\n }\r\n nt.g[k].gain.value = 0;\r\n }\r\n },\r\n _limitVoices:(ch,n)=>{\r\n this.notetab.sort(function(n1,n2){\r\n if(n1.f!=n2.f) return n1.f-n2.f;\r\n if(n1.e!=n2.e) return n2.e-n1.e;\r\n return n2.t-n1.t;\r\n });\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n var nt=this.notetab[i];\r\n if(this.actx.currentTime>nt.e || i>=(this.voices-1)){\r\n this._pruneNote(nt);\r\n this.notetab.splice(i,1);\r\n }\r\n }\r\n },\r\n _note:(t,ch,n,v,p)=>{\r\n let out,sc,pn;\r\n const o=[],g=[],vp=[],fp=[],r=[];\r\n const f=440*Math.pow(2,(n-69 + this.masterTuningC + this.tuningC[ch] + (this.masterTuningF + this.tuningF[ch])/8192)/12);\r\n this._limitVoices(ch,n);\r\n for(let i=0;i10)\r\n out=g[pn.g-11].gain, sc=1, fp[i]=fp[pn.g-11]*pn.t+pn.f;\r\n else if(o[pn.g-1].frequency)\r\n out=o[pn.g-1].frequency, sc=fp[pn.g-1], fp[i]=fp[pn.g-1]*pn.t+pn.f;\r\n else\r\n out=o[pn.g-1].playbackRate, sc=fp[pn.g-1]/440, fp[i]=fp[pn.g-1]*pn.t+pn.f;\r\n switch(pn.w[0]){\r\n case \"n\":\r\n o[i]=this.actx.createBufferSource();\r\n o[i].buffer=this.noiseBuf[pn.w];\r\n o[i].loop=true;\r\n o[i].playbackRate.value=fp[i]/440;\r\n if(pn.p!=1)\r\n this._setParamTarget(o[i].playbackRate,fp[i]/440*pn.p,t,pn.q);\r\n if (o[i].detune) {\r\n this.chmod[ch].connect(o[i].detune);\r\n o[i].detune.value=this.bend[ch];\r\n }\r\n break;\r\n default:\r\n o[i]=this.actx.createOscillator();\r\n o[i].frequency.value=fp[i];\r\n if(pn.p!=1)\r\n this._setParamTarget(o[i].frequency,fp[i]*pn.p,t,pn.q);\r\n if(pn.w[0]==\"w\")\r\n o[i].setPeriodicWave(this.wave[pn.w]);\r\n else\r\n o[i].type=pn.w;\r\n if (o[i].detune) {\r\n this.chmod[ch].connect(o[i].detune);\r\n o[i].detune.value=this.bend[ch];\r\n }\r\n break;\r\n }\r\n g[i]=this.actx.createGain();\r\n r[i]=pn.r;\r\n o[i].connect(g[i]); g[i].connect(out);\r\n vp[i]=sc*pn.v;\r\n if(pn.k)\r\n vp[i]*=Math.pow(2,(n-60)/12*pn.k);\r\n if(pn.a){\r\n g[i].gain.value=0;\r\n g[i].gain.setValueAtTime(0,t);\r\n g[i].gain.linearRampToValueAtTime(vp[i],t+pn.a);\r\n }\r\n else\r\n g[i].gain.setValueAtTime(vp[i],t);\r\n this._setParamTarget(g[i].gain,pn.s*vp[i],dt,pn.d);\r\n o[i].start(t);\r\n if(this.rhythm[ch]){\r\n\r\n o[i].onended = ()=>{\r\n try {\r\n if (o[i].detune) this.chmod[ch].disconnect(o[i].detune);\r\n }\r\n catch(e){}\r\n };\r\n o[i].stop(t+p[0].d*this.releaseRatio);\r\n }\r\n }\r\n if(!this.rhythm[ch])\r\n this.notetab.push({t:t,e:99999,ch:ch,n:n,o:o,g:g,t2:t+pn.a,v:vp,r:r,f:0});\r\n },\r\n _setParamTarget:(p,v,t,d)=>{\r\n if(d!=0)\r\n p.setTargetAtTime(v,t,d);\r\n else\r\n p.setValueAtTime(v,t);\r\n },\r\n _releaseNote:(nt,t)=>{\r\n if(nt.ch!=9){\r\n for(let k=nt.g.length-1;k>=0;--k){\r\n nt.g[k].gain.cancelScheduledValues(t);\r\n if(t==nt.t2)\r\n nt.g[k].gain.setValueAtTime(nt.v[k],t);\r\n else if(t{\r\n this.chmod[ch].gain.setValueAtTime(v*100/127,this._tsConv(t));\r\n },\r\n setChVol:(ch,v,t)=>{\r\n this.vol[ch]=3*v*v/(127*127);\r\n this.chvol[ch].gain.setValueAtTime(this.vol[ch]*this.ex[ch],this._tsConv(t));\r\n },\r\n setPan:(ch,v,t)=>{\r\n if(this.chpan[ch])\r\n this.chpan[ch].pan.setValueAtTime((v-64)/64,this._tsConv(t));\r\n },\r\n setExpression:(ch,v,t)=>{\r\n this.ex[ch]=v*v/(127*127);\r\n this.chvol[ch].gain.setValueAtTime(this.vol[ch]*this.ex[ch],this._tsConv(t));\r\n },\r\n setSustain:(ch,v,t)=>{\r\n this.sustain[ch]=v;\r\n t=this._tsConv(t);\r\n if(v<64){\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n const nt=this.notetab[i];\r\n if(t>=nt.t && nt.ch==ch && nt.f==1)\r\n this._releaseNote(nt,t);\r\n }\r\n }\r\n },\r\n allSoundOff:(ch)=>{\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n const nt=this.notetab[i];\r\n if(nt.ch==ch){\r\n this._pruneNote(nt);\r\n this.notetab.splice(i,1);\r\n }\r\n }\r\n },\r\n resetAllControllers:(ch)=>{\r\n this.bend[ch]=0; this.ex[ch]=1.0;\r\n this.rpnidx[ch]=0x3fff; this.sustain[ch]=0;\r\n if(this.chvol[ch]){\r\n this.chvol[ch].gain.value=this.vol[ch]*this.ex[ch];\r\n this.chmod[ch].gain.value=0;\r\n }\r\n },\r\n setBendRange:(ch,v)=>{\r\n this.brange[ch]=v;\r\n },\r\n setProgram:(ch,v)=>{\r\n if(this.debug)\r\n console.log(\"Pg(\"+ch+\")=\"+v);\r\n this.pg[ch]=v;\r\n },\r\n _tsConv:(t)=>{\r\n if(t==undefined||t<=0){\r\n t=0;\r\n if(this.actx)\r\n t=this.actx.currentTime;\r\n }\r\n else{\r\n if(this.tsmode)\r\n t=t*.001-this.tsdiff;\r\n }\r\n return t;\r\n },\r\n setBend:(ch,v,t)=>{\r\n t=this._tsConv(t);\r\n const br=this.brange[ch]*100/127;\r\n this.bend[ch]=(v-8192)*br/8192;\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n const nt=this.notetab[i];\r\n if(nt.ch==ch){\r\n for(let k=nt.o.length-1;k>=0;--k){\r\n if(nt.o[k].frequency)\r\n if (nt.o[k].detune) nt.o[k].detune.setValueAtTime(this.bend[ch],t);\r\n }\r\n }\r\n }\r\n },\r\n noteOff:(ch,n,t)=>{\r\n if(this.rhythm[ch])\r\n return;\r\n t=this._tsConv(t);\r\n for(let i=this.notetab.length-1;i>=0;--i){\r\n const nt=this.notetab[i];\r\n if(t>=nt.t && nt.ch==ch && nt.n==n && nt.f==0){\r\n nt.f=1;\r\n if(this.sustain[ch]<64)\r\n this._releaseNote(nt,t);\r\n }\r\n }\r\n },\r\n noteOn:(ch,n,v,t)=>{\r\n if(v==0){\r\n this.noteOff(ch,n,t);\r\n return;\r\n }\r\n t=this._tsConv(t);\r\n if(this.rhythm[ch]){\r\n if(n>=35&&n<=81)\r\n this._note(t,ch,n,v,this.drummap[n-35].p);\r\n return;\r\n }\r\n this._note(t,ch,n,v,this.program[this.pg[ch]].p);\r\n },\r\n setTsMode:(tsmode)=>{\r\n this.tsmode=tsmode;\r\n },\r\n send:(msg,t)=>{ /* send midi message */\r\n const ch=msg[0]&0xf;\r\n const cmd=msg[0]&~0xf;\r\n if(cmd<0x80||cmd>=0x100)\r\n return;\r\n if(this.audioContext.state==\"suspended\"){\r\n this.audioContext.resume();\r\n }\r\n switch(cmd){\r\n case 0xb0: /* ctl change */\r\n switch(msg[1]){\r\n case 1: this.setModulation(ch,msg[2],t); break;\r\n case 7: this.setChVol(ch,msg[2],t); break;\r\n case 10: this.setPan(ch,msg[2],t); break;\r\n case 11: this.setExpression(ch,msg[2],t); break;\r\n case 64: this.setSustain(ch,msg[2],t); break;\r\n case 98: case 99: this.rpnidx[ch]=0x3fff; break; /* nrpn lsb/msb */\r\n case 100: this.rpnidx[ch]=(this.rpnidx[ch]&0x3f80)|msg[2]; break; /* rpn lsb */\r\n case 101: this.rpnidx[ch]=(this.rpnidx[ch]&0x7f)|(msg[2]<<7); break; /* rpn msb */\r\n case 6: /* data entry msb */\r\n switch (this.rpnidx[ch]) {\r\n case 0:\r\n this.brange[ch]=(msg[2]<<7)+(this.brange[ch]&0x7f);\r\n break;\r\n case 1:\r\n this.tuningF[ch]=(msg[2]<<7)+((this.tuningF[ch]+0x2000)&0x7f)-0x2000;\r\n break;\r\n case 2:\r\n this.tuningC[ch]=msg[2]-0x40;\r\n break;\r\n }\r\n break;\r\n case 38: /* data entry lsb */\r\n switch (this.rpnidx[ch]) {\r\n case 0:\r\n this.brange[ch]=(this.brange[ch]&0x3f80)|msg[2];\r\n break;\r\n case 1:\r\n this.tuningF[ch]=((this.tuningF[ch]+0x2000)&0x3f80)|msg[2]-0x2000;\r\n break;\r\n case 2: break;\r\n }\r\n break;\r\n case 120: /* all sound off */\r\n case 123: /* all notes off */\r\n case 124: case 125: case 126: case 127: /* omni off/on mono/poly */\r\n this.allSoundOff(ch);\r\n break;\r\n case 121: this.resetAllControllers(ch); break;\r\n }\r\n break;\r\n case 0xc0: this.setProgram(ch,msg[1]); break;\r\n case 0xe0: this.setBend(ch,(msg[1]+(msg[2]<<7)),t); break;\r\n case 0x90: this.noteOn(ch,msg[1],msg[2],t); break;\r\n case 0x80: this.noteOff(ch,msg[1],t); break;\r\n case 0xf0:\r\n if (msg[0] == 0xff) {\r\n this.reset();\r\n break;\r\n }\r\n if(msg[0]!=254 && this.debug){\r\n var ds=[];\r\n for(let ii=0;ii= 8) { // Master Fine Tuning\r\n this.masterTuningF = msg[6]*0x80 + msg[5] - 8192;\r\n }\r\n if (msg[4]==4 && msg.length >= 8) { // Master Coarse Tuning\r\n this.masterTuningC = msg[6]-0x40;\r\n }\r\n }\r\n if(msg[1]==0x41&&msg[3]==0x42&&msg[4]==0x12&&msg[5]==0x40){\r\n if((msg[6]&0xf0)==0x10&&msg[7]==0x15){\r\n const c=[9,0,1,2,3,4,5,6,7,8,10,11,12,13,14,15][msg[6]&0xf];\r\n this.rhythm[c]=msg[8];\r\n }\r\n }\r\n }\r\n break;\r\n }\r\n },\r\n _createWave:(w)=>{\r\n const imag=new Float32Array(w.length);\r\n const real=new Float32Array(w.length);\r\n for(let i=1;i{\r\n return this.actx;\r\n },\r\n setAudioContext:(actx,dest)=>{\r\n this.audioContext=this.actx=actx;\r\n this.dest=dest;\r\n if(!dest)\r\n this.dest=actx.destination;\r\n this.tsdiff=performance.now()*.001-this.actx.currentTime;\r\n console.log(\"TSDiff:\"+this.tsdiff);\r\n this.out=this.actx.createGain();\r\n this.comp=this.actx.createDynamicsCompressor();\r\n var blen=this.actx.sampleRate*.5|0;\r\n this.convBuf=this.actx.createBuffer(2,blen,this.actx.sampleRate);\r\n this.noiseBuf={};\r\n this.noiseBuf.n0=this.actx.createBuffer(1,blen,this.actx.sampleRate);\r\n this.noiseBuf.n1=this.actx.createBuffer(1,blen,this.actx.sampleRate);\r\n var d1=this.convBuf.getChannelData(0);\r\n var d2=this.convBuf.getChannelData(1);\r\n var dn=this.noiseBuf.n0.getChannelData(0);\r\n var dr=this.noiseBuf.n1.getChannelData(0);\r\n for(let i=0;i\r\n `;\r\n\r\n this.getAttr = (n,def)=>{\r\n let v=this.getAttribute(n);\r\n if(v==\"\"||v==null) return def;\r\n switch(typeof(def)){\r\n case \"number\":\r\n if(v==\"true\") return 1;\r\n v=+v;\r\n if(isNaN(v)) return 0;\r\n return v;\r\n }\r\n return v;\r\n };\r\n\r\n this.canvas = div.children[0];\r\n this.appendChild(div);\r\n WebAudioTinySynthCore.bind(this)(this);\r\n const plist=this.properties;\r\n for(let k in plist){\r\n const v = plist[k];\r\n if(v.observer){\r\n this[\"_\"+k] = v.value;\r\n Object.defineProperty(this, k, {\r\n get:()=>{return this[\"_\"+k]},\r\n set:(val)=>{\r\n this[\"_\"+k] = val;\r\n this[v.observer]();\r\n }\r\n });\r\n }\r\n else{\r\n this[k]=v;\r\n }\r\n }\r\n for(let k in plist){\r\n const v = plist[k];\r\n this[k] = this.getAttr(k,v.value);\r\n }\r\n this.setQuality(1);\r\n this.init();\r\n this._guiInit.bind(this)();\r\n setInterval(this._guiUpdate.bind(this),100);\r\n }\r\n }\r\n window.customElements.define('webaudio-tinysynth', WebAudioTinySynthElement);\r\n}\r\n\r\nclass WebAudioTinySynth {\r\n constructor(opt){\r\n WebAudioTinySynthCore.bind(this)(this);\r\n for(let k in this.properties){\r\n this[k]=this.properties[k].value;\r\n }\r\n this.setQuality(1);\r\n if(opt){\r\n if(opt.useReverb!=undefined)\r\n this.useReverb=opt.useReverb;\r\n if(opt.quality!=undefined)\r\n this.setQuality(opt.quality);\r\n if(opt.voices!=undefined)\r\n this.setVoices(opt.voices);\r\n }\r\n this.init();\r\n }\r\n}\r\n\r\nif(typeof exports === 'object' && typeof module !== 'undefined'){\r\n module.exports = WebAudioTinySynth;\r\n}\r\nelse if(typeof define === 'function' && define.amd){\r\n define(function(){\r\n return WebAudioTinySynth;\r\n });\r\n}\r\nelse{\r\n window.WebAudioTinySynth = WebAudioTinySynth;\r\n}\r\n\r\n})(this);\r\n","export type ICancel = () => void\nexport type FSubscribe = (value: T) => any\n\nexport class Value {\n $: T\n protected reactions: Set>\n stopKeeping: ICancel\n\n constructor(value: T = undefined) {\n this.$ = value\n }\n\n set(value: T) {\n this.$ = value\n this.poke()\n\n return this\n }\n\n on(subscribe: FSubscribe): ICancel {\n if (this.reactions === undefined) {\n this.reactions = new Set()\n }\n\n this.reactions.add(subscribe)\n\n subscribe(this.$)\n return () => this.reactions.delete(subscribe)\n }\n\n subscribe(subscribe: FSubscribe) {\n return this.on(subscribe)\n }\n\n log(msg) {\n this.on(() => console.log(msg, this.$))\n return this\n }\n\n poke() {\n if (this.reactions === undefined) return\n\n for (let callback of this.reactions) {\n callback(this.$)\n }\n\n return this\n }\n\n do(fn: () => void) {\n // never gonna make you cry\n fn()\n return this\n }\n re(fn: (value: T) => void) {\n // never gonna give you up\n this.on(fn)\n\n return this\n }\n me() {\n return new Value(this.$)\n }\n fa(\n v: Value,\n transform?: (value: TT) => T,\n filter?: (value: TT) => boolean\n ) {\n // let you down\n v.on((state) => {\n if (filter) {\n if (!filter(state)) return\n }\n if (transform) {\n this.set(transform(state))\n } else {\n // @ts-ignore\n this.set(state)\n }\n })\n\n return this\n }\n la(timing: number, fn: (i: number) => void) {\n // never gonna turn around\n let i = 0\n setInterval(() => {\n fn(i++)\n }, timing)\n\n return this\n }\n\n save(where: string) {\n // or desert you?\n try {\n const v = JSON.parse(localStorage.getItem(where))\n\n if (v !== undefined && v !== null) {\n this.set(v)\n }\n } catch (ex) {}\n\n this.on((v) => {\n localStorage.setItem(where, JSON.stringify(v))\n })\n return this\n }\n}\n","import { Value } from 'src/value'\nimport { Uniform } from 'three'\n\nexport const tick = new Value(0)\nexport const delta = new Value(0)\nexport const timing = new Value(0)\n\nexport function Loop($t) {\n delta.set(($t - timing.$) / 1000)\n timing.set($t)\n\n tick.set(tick.$ + 1)\n}\n\nexport const timeUniform = new Uniform(0.0)\ntiming.on(($t) => {\n timeUniform.value = Math.floor(performance.now())\n})\n\nexport function Timer(timeout: number, fn: () => void) {\n const intv = setInterval(fn, timeout)\n\n return () => clearInterval(intv)\n}\n","import { get, set } from 'idb-keyval'\nimport { Uniform } from 'three'\nimport WebAudioTinySynth from 'webaudio-tinysynth'\nimport { tick } from '../shader/time'\nimport { Value } from '../value'\nimport { EMidiInstrument } from './midi'\n\nlet lastVolume = 1\n\nexport const audio = document.getElementById('bgm') as HTMLAudioElement\nexport const audio_buffer = new Value()\nexport const audio_name = new Value('')\nexport const volume = new Value(1)\n .do(async () => {\n get('$audio_volume').then((v) => {\n volume.set(v)\n })\n })\n .re((v) => {\n if (v === undefined) return\n\n set('$audio_volume', v)\n\n lastVolume = v\n audio.volume = v\n })\n\nexport const mute = new Value(false)\n .do(async () => {\n get('$audio_mute').then((v) => {\n mute.set(v)\n })\n })\n .re((v) => {\n if (v === undefined) return\n\n set('$audio_mute', v)\n\n audio.muted = v\n })\n\nexport const context = new Value()\nlet started = false\n\nexport const synth = new Value(undefined)\n\nexport const makeAudioReady = async () => {\n if (synth.$ !== undefined) return\n\n const s = new WebAudioTinySynth({\n quality: 0,\n voices: 1024,\n useReverb: 1,\n })\n await s.ready()\n synth.set(s)\n}\n\naudio.onplay = function () {\n if (started) return\n started = true\n context.set(new AudioContext())\n const src = context.$.createMediaElementSource(audio)\n\n const analyser = context.$.createAnalyser()\n src.connect(analyser)\n\n analyser.connect(context.$.destination)\n\n analyser.fftSize = 512\n\n const bufferLength = analyser.frequencyBinCount\n const dataArray = new Uint8Array(bufferLength)\n\n function sum(arr: Uint8Array) {\n let val = 0\n for (let i = 0; i < arr.length; i++) {\n val = val + arr[i]\n }\n\n return val\n }\n\n tick.on(() => {\n analyser.getByteFrequencyData(dataArray)\n const lowerHalfArray = dataArray.slice(0, dataArray.length / 2 - 1)\n const upperHalfArray = dataArray.slice(\n dataArray.length / 2 - 1,\n dataArray.length - 1\n )\n\n lowerAvg.set(sum(lowerHalfArray) / lowerHalfArray.length)\n upperAvg.set(sum(upperHalfArray) / upperHalfArray.length)\n })\n}\n\naudio.addEventListener('canplaythrough', () => {\n end.set(audio.duration)\n})\n\nexport const seconds = new Value(0)\nexport const end = new Value(1)\nexport const upperUniform = new Uniform(0)\nexport const lowerUniform = new Uniform(0)\n\nexport const upperAvg = new Value(0)\nupperAvg.on(($ua) => (upperUniform.value = $ua))\n\nexport const lowerAvg = new Value(0)\nlowerAvg.on(($la) => (lowerUniform.value = $la))\n\ntick.on(() => {\n if (lastVolume !== audio.volume) {\n lastVolume = audio.volume\n volume.set(lastVolume)\n }\n if (audio.muted !== mute.$) {\n mute.set(audio.muted)\n }\n\n if (seconds.$ !== audio.currentTime) {\n seconds.set(audio.currentTime)\n }\n})\n\nconst $midi = [0x90, 0, 0]\nlet attempt = false\nexport const MIDI = (\n instrument: EMidiInstrument,\n note: number,\n velocity: number\n) => {\n if (audio.muted) return false\n\n if (synth.$ === undefined) {\n return false\n }\n // ensure channel has that instrument set\n synth.$.setProgram(0, instrument)\n\n $midi[1] = note\n $midi[2] = Math.round(velocity * 100 * audio.volume)\n\n synth.$.send($midi)\n\n $midi[2] = 0\n synth.$.send($midi)\n\n return false\n}\n\nObject.assign(window, { MIDI })\n\nexport function Tune(timer: number, count: number, skip: (i) => void) {\n let i = 0\n const intv = setInterval(() => {\n if (i >= count) {\n clearInterval(intv)\n return\n }\n skip(i++)\n }, timer)\n}\n\nexport function Chirp(ins = 81, note = 90, volume = 0.5) {\n Tune(25, 15, (i) => {\n if (i % 3 === 0) return\n MIDI(ins, note + (i % 5), volume)\n })\n}\n\nconst view = new DataView(new ArrayBuffer(4))\n\nexport function Play(noise: number) {\n view.setInt32(0, noise)\n\n let i = 0\n const intv = setInterval(() => {\n if (i >= 8) {\n return clearInterval(intv)\n }\n if (view.getUint8(3) & (1 << i)) {\n MIDI(view.getUint8(0), view.getUint8(1), view.getUint8(2) / 256)\n }\n i++\n }, (1 / 8) * 1000)\n}\n","import { Value } from 'src/value'\r\n\r\nexport const key_down = new Value('')\r\nexport const key_up = new Value('')\r\nexport const key_map = new Value({})\r\n\r\nfunction bounce(e: KeyboardEvent) {\r\n return (\r\n // @ts-ignore\r\n e.target.tagName === 'INPUT' ||\r\n // @ts-ignore\r\n e.target.closest('.CodeMirror') !== null\r\n )\r\n}\r\n\r\nwindow.addEventListener('keydown', (e) => {\r\n if (bounce(e)) return\r\n\r\n e.preventDefault()\r\n key_down.set(e.key)\r\n key_map.$[e.key] = true\r\n key_map.poke()\r\n})\r\n\r\nwindow.addEventListener('keyup', (e) => {\r\n if (bounce(e)) return\r\n\r\n key_up.set(e.key)\r\n key_map.$[e.key] = false\r\n key_map.poke()\r\n})\r\n","// updates inputs for gamepad\n// eventually will want to support local multiplayer with this\n// piping input to an alias map to a controller's actions\n\nimport { key_down, key_up } from 'src/input/keyboard'\nimport { Value } from 'src/value'\n\nexport const pad_axes = new Value({\n lefthorizontal: 0,\n leftvertical: 0,\n rightvertical: 0,\n righthorizontal: 0,\n})\n\nexport const sensitivity = new Value(0.09)\nexport const pads_disabled = new Value({})\nexport const pads = new Value({})\n\nexport const pads_activate = new Value(-1).on((i) => {\n if (i < 0) return\n delete pads_disabled.$[i]\n pads_disabled.poke()\n})\nexport const pad_disable = new Value(-1).on((i) => {\n if (i < 0) return\n\n pads_disabled.$[i] = true\n pads_disabled.poke()\n})\n\nexport const pad_down = new Value(-1)\nexport const pad_up = new Value(-1)\nexport const pad = new Value({})\n\nexport const pad_connected = new Value(true)\n\npads.on(\n ($pads) =>\n !pad_connected.$ && pad_connected.set(Object.keys($pads).length > 0)\n)\n\nwindow.addEventListener(`gamepadconnected`, ({ gamepad }: any) => {\n pads.$[gamepad.index] = gamepad\n pads.poke()\n})\n\nwindow.addEventListener(`gamepaddisconnected`, ({ gamepad }: any) => {\n delete pads.$[gamepad.index]\n pads.poke()\n})\n\nexport const xbox = {\n 0: `a`,\n 1: `b`,\n 2: `x`,\n 3: `y`,\n 4: `leftshoulder`,\n 5: `rightshoulder`,\n 6: `lefttrigger`,\n 7: `righttrigger`,\n 8: `select`,\n 9: `start`,\n 10: `leftstick`,\n 11: `rightstick`,\n 12: `up`,\n 13: `down`,\n 14: `left`,\n 15: `right`,\n}\n// analogs that will require\nexport const AXIS = {\n 0: `lefthorizontal`,\n 1: `leftvertical`,\n 2: `righthorizontal`,\n 3: `rightvertical`,\n}\n\nexport const input_map = {\n a: 'Enter',\n b: 'Escape',\n start: '~',\n\n left: 'ArrowLeft',\n right: 'ArrowRight',\n down: 'ArrowDown',\n up: 'ArrowUp',\n leftshoulder: 'f',\n rightshoulder: 'r',\n leftrigger: 'Shift',\n}\nlet last_dirty = false\nsetInterval(() => {\n const $ps = pad.$\n\n let changes = 0\n\n for (let gpad of navigator.getGamepads()) {\n if (!gpad) continue\n\n if (pads_disabled.$[gpad.index]) continue\n for (let i = 0; i < gpad.buttons.length; i++) {\n const state = gpad.buttons[i].pressed || gpad.buttons[i].touched\n if ($ps[i] !== state) {\n changes++\n\n if (state) {\n pad_down.set(i)\n } else {\n pad_up.set(i)\n }\n }\n }\n\n //axis\n const $ax = pad_axes.$\n for (let i = 0; i < gpad.axes.length; i++) {\n if (Math.abs(gpad.axes[i]) > sensitivity.$) {\n changes++\n $ax[i] = gpad.axes[i]\n $ax[AXIS[i]] = gpad.axes[i]\n } else if (last_dirty) {\n $ax[i] = 0\n $ax[AXIS[i]] = 0\n pad_axes.poke()\n }\n }\n }\n\n if (changes > 0) {\n pad.poke()\n pad_axes.poke()\n last_dirty = true\n } else {\n last_dirty = false\n }\n}, 100)\n\npad_down.on((i) => {\n if (i === -1) return\n\n const $p = pad.$\n const k = xbox[i]\n\n $p[k] = $p[i] = true\n\n const input = input_map[k]\n\n if (input === undefined) return\n\n key_down.set(input)\n})\n\npad_up.on((i) => {\n if (i === -1) return\n\n const $p = pad.$\n const k = xbox[i]\n\n $p[i] = false\n $p[xbox[i]] = false\n\n const input = input_map[k]\n\n if (input === undefined) return\n\n key_up.set(input)\n})\n","export const ATOM_COUNT = 256 * 256\n\n// For GamePlay, max impacts for entity, the impacts still happen for velocity purposes\nexport const IMPACTS_MAX_PER = 8\nexport const TIMELINE_MAX = 1024 * 8\n\nexport const SIZE = 0.001\nexport const FACES = 1\nexport const MIN_POSE_VALUE = 0.15\nexport const INFRINGEMENT = 1\n// max int 32, which are used in the SharedArrayBuffer\nexport const NORMALIZER = 0x7fffffff\nexport const UNIVERSALS = [\n 0, // time\n 10, // player size\n 0, // idle\n 0x0055ff, //clear color\n 0, // fae startx\n 0, // fae starty\n 0, // fae startz\n 0, // music start time\n 0, // rot x\n 0, // rot y\n 0, // rot z\n // default universal cage\n -NORMALIZER / 2,\n -NORMALIZER / 2,\n -NORMALIZER / 2,\n NORMALIZER,\n NORMALIZER,\n NORMALIZER,\n // offset\n 0,\n 0,\n 0,\n // ERealmState\n 1, // RUNNING\n -1, // no avatar\n 1, // thrust strength is 1 normally\n 0, // SCORE\n 0,\n 0,\n 0, // gravity\n 0x5522ff, // fae color\n 0.15 * NORMALIZER, // fae hue variance,\n ...new Array(10).fill(0), // fae hands id\n]\n\nexport const CACHE = 'v2'\n\nexport const dotTheia = [\n 'aero',\n 'overworld',\n 'gate',\n 'starvoyage',\n 'forest',\n 'flood',\n 'birthday',\n 'city',\n 'flappy',\n]\nexport const rootTheia = 'agoblinking/overworld'\n\nexport const PAD_SPEED = 0.5\n\nexport const FAE_SCALE = 100\n\nexport function FaeUnits(x: number) {\n return x / FAE_SCALE\n}\n\nexport const YGGDRASIL = '/bifrost'\n\nexport const SENSE_DISTANCE = {\n see: 60000,\n hear: 30000,\n felt: 1000,\n}\n\nexport const MAX_NORMAL_FORCE = 1000\n","import { Value } from './value'\n\ninterface ISteam extends EventTarget {\n saves: any[]\n workshop: any[]\n post: (data: string) => void\n}\n\n// @ts-ignore\nexport const steam = new Value(window.steam)\n\nexport const steam_open = new Value([]).re((arr) => {\n if (!arr || !steam.$) return\n const [url, where] = arr\n if (!url) return\n\n if (where === '_self') {\n window.open(url, where)\n return\n }\n\n steam.$.post(`open|${url}|${where}`)\n})\n","import { dotTheia } from 'src/config'\r\nimport { steam, steam_open } from 'src/steam'\r\nimport { Value } from 'src/value'\r\n\r\nexport const url = new Value(\r\n decodeURI(window.location.pathname).slice(1).split('/')\r\n)\r\n\r\nexport const pathname = new Value('').fa(url, (v) => {\r\n const val = v.join('/')\r\n return val === '' ? '/' : val\r\n})\r\n\r\nexport const hasSharedArrayBuffer = window.SharedArrayBuffer !== undefined\r\n\r\nexport const history = new Value([]).save('history')\r\nexport const favorites = new Value([]).save('favorites')\r\nexport const isFavorite = new Value(false).fa(\r\n favorites,\r\n (f) => f.indexOf(pathname.$) !== -1\r\n)\r\nexport const curated = dotTheia\r\n\r\nif (hasSharedArrayBuffer) {\r\n document.body.classList.add('sab')\r\n}\r\n\r\nexport const mobile =\r\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\r\n navigator.userAgent\r\n )\r\n\r\nexport const options = new Value(\r\n new Set(window.location.search.toUpperCase().slice(1).split('|'))\r\n)\r\n\r\nexport const isVR = new Value(false)\r\n// @ts-ignore\r\nnavigator.xr?.isSessionSupported('immersive-vr').then(function (supported) {\r\n isVR.set(supported)\r\n})\r\n\r\nexport const multiplayer = window.location.hash.slice(1)\r\n\r\nexport const isQuest =\r\n navigator.userAgent.indexOf('OculusBrowser') !== -1 ||\r\n navigator.userAgent.indexOf('Android') !== -1\r\n\r\nexport const browserOpen = new Value().re((arr) => {\r\n if (!arr) return\r\n\r\n const [url, where] = arr\r\n\r\n if (steam.$) steam_open.set([url, where])\r\n else window.open(url, where)\r\n})\r\n\r\nconst hix = history.$.indexOf(pathname.$)\r\nif (hix !== -1) {\r\n history.$.splice(hix, 1)\r\n}\r\n\r\nhistory.$.push(pathname.$)\r\nhistory.$.splice(0, history.$.length - 5)\r\nhistory.poke()\r\n\r\nexport function Pin() {\r\n const idx = favorites.$.indexOf(pathname.$)\r\n if (idx === -1) {\r\n favorites.$.push(pathname.$)\r\n } else {\r\n favorites.$.splice(idx, 1)\r\n }\r\n\r\n favorites.poke()\r\n}\r\n","import { Loop, tick } from 'src/shader/time'\nimport { Value } from 'src/value'\nimport {\n AmbientLight,\n DirectionalLight,\n Group,\n PerspectiveCamera,\n Scene,\n WebGLRenderer,\n} from 'three'\nimport { isQuest } from './input/browser'\n\nexport const scene = new Value(new Scene())\nexport const camera = new PerspectiveCamera(\n 75,\n window.innerWidth / window.innerHeight,\n 0.01,\n 10000000\n)\n\nexport const body = new Value(new Group())\n\nbody.$.position.set(0, 0.15, 2.5)\nconst sun = new DirectionalLight(0xffffff, 0.35)\nsun.position.set(1, 1, 1)\nconst sun2 = sun.clone()\nsun.position.set(-1, 1, -1)\nscene.$.add(body.$.add(camera), new AmbientLight(0xffffff, 0.5), sun, sun2)\n\nexport const renderer = new WebGLRenderer({\n antialias: !isQuest,\n preserveDrawingBuffer: !isQuest,\n})\n\nrenderer.domElement.id = 'three'\n\nrenderer.domElement.addEventListener('mousedown', () => {\n // @ts-ignore\n document.activeElement.blur()\n renderer.domElement.focus()\n})\n\nrenderer.setClearColor(0x0055ff, 1)\n\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.domElement)\n\nlet resize = false\n\nwindow.addEventListener('resize', () => {\n resize = true\n})\n\ntick.on(() => {\n renderer.render(scene.$, camera)\n\n if (resize) {\n renderer.setSize(window.innerWidth, window.innerHeight)\n camera.aspect = window.innerWidth / window.innerHeight\n camera.updateProjectionMatrix()\n resize = false\n }\n})\n\nrenderer.setAnimationLoop(Loop)\n","import { renderer } from 'src/render'\nimport { Value } from 'src/value'\nimport { Vector2, Vector3 } from 'three'\n\n// Normalized Mouse Position from center of screen\nexport const mouse_pos = new Value(new Vector2())\nexport const mouse_world = new Value(new Vector3())\nexport const mouse_left = new Value(false)\nexport const mouse_right = new Value(false)\nexport const mouse_page = new Value(new Vector2())\nexport const middle_mouse_toggle = new Value(false)\n\nfunction Down(e) {\n switch (e.button) {\n case undefined && e.touches.length === 1:\n case 1:\n middle_mouse_toggle.set(!middle_mouse_toggle.$)\n e.preventDefault()\n break\n case 0:\n mouse_left.set(true)\n break\n\n case undefined && e.touches.length > 1:\n case 2:\n mouse_right.set(true)\n\n break\n }\n}\n\nfunction Up(e) {\n switch (e.button) {\n case undefined && e.touches.length === 1:\n case 0:\n mouse_left.set(false)\n break\n\n case undefined && e.touches.length > 1:\n case 2:\n mouse_right.set(false)\n\n break\n }\n}\n\nfunction Move(e: MouseEvent) {\n mouse_pos.set(\n mouse_pos.$.set(\n (e.x / renderer.domElement.width) * 2 - 1,\n -(e.y / renderer.domElement.height) * 2 + 1\n )\n )\n}\n\nexport const mouse_wheel = new Value(0)\nrenderer.domElement.addEventListener('mousewheel', (e: any) => {\n mouse_wheel.set(-e.wheelDelta)\n})\n\nwindow.addEventListener('mousemove', (e) => {\n mouse_page.set(mouse_page.$.set(e.pageX, e.pageY))\n})\n\nrenderer.domElement.addEventListener('mousemove', Move)\nrenderer.domElement.addEventListener('mouseup', Up)\nrenderer.domElement.addEventListener('touchend', Up)\n\nrenderer.domElement.addEventListener('mousedown', Down)\nrenderer.domElement.addEventListener('touchstart', Down)\nrenderer.domElement.addEventListener(\n 'contextmenu',\n function (evt) {\n evt.preventDefault()\n },\n false\n)\n","import { key_down, key_up } from 'src/input/keyboard'\nimport { middle_mouse_toggle, mouse_left, mouse_right } from 'src/input/mouse'\nimport { Value } from 'src/value'\nimport { Euler } from 'three'\nimport { MIDI } from './audio'\n\nconst _euler = new Euler()\nconst _PI_2 = Math.PI / 2\nexport const looking = new Value(false).fa(middle_mouse_toggle).re((state) => {\n if (state) document.body.classList.add('looking')\n else document.body.classList.remove('looking')\n\n // if (state) {\n // renderer.domElement.requestPointerLock()\n // } else {\n // renderer.domElement.ownerDocument.exitPointerLock()\n // }\n\n MIDI(90, 60 - (state ? 10 : 0), 0.25)\n})\n\n// renderer.domElement.ownerDocument.addEventListener('mousemove', (event) => {\n// if (!looking.$) return\n\n// // feed as mouse coords for the smooth cam\n\n// const movementX =\n// // @ts-ignore\n// event.movementX || event.mozMovementX || event.webkitMovementX || 0\n\n// const movementY =\n// // @ts-ignore\n// event.movementY || event.mozMovementY || event.webkitMovementY || 0\n// mouse_pos.$.set(movementX, -movementY).multiplyScalar(0.01)\n// })\n// TODO: add me, fa, la, te\n\nexport const loading = new Value(false).re((state) => {\n document.body.classList.toggle('loading', state)\n})\n\nexport const left_forward = new Value(false)\n .re((state) => {\n MIDI(80, 30 - (state ? 10 : 0), 0.32)\n })\n .fa(mouse_left)\n\nexport const right_forward = new Value(false)\n .re((state) => {\n MIDI(80, 30 - (state ? 10 : 0), 0.32)\n })\n .fa(mouse_right)\n\nexport const left_grabbed = new Value(undefined)\n\nexport const left_grab = new Value(false)\n\nexport const right_grab = new Value(false)\nexport const left_use = new Value(false)\nexport const right_use = new Value(false)\n\nkey_down.on((k) => {\n switch (k) {\n case '1':\n left_grab.$ === false && left_grab.set(true)\n break\n case '3':\n right_grab.$ === false && right_grab.set(true)\n break\n case 'q':\n left_use.$ === false && left_use.set(true)\n break\n case 'e':\n right_use.$ === false && right_use.set(true)\n break\n }\n})\n\nkey_up.on((k) => {\n switch (k) {\n case '1':\n left_grab.set(false)\n break\n case '3':\n right_grab.set(false)\n break\n case 'q':\n left_use.set(false)\n break\n case 'e':\n right_use.set(false)\n break\n }\n})\n","import { Group, Object3D } from 'three'\n\nexport enum vr_keys {\n 'wrist',\n 'thumb-metacarpal',\n 'thumb-phalanx-proximal',\n 'thumb-phalanx-distal',\n 'thumb-tip',\n 'index-finger-metacarpal',\n 'index-finger-phalanx-proximal',\n 'index-finger-phalanx-intermediate',\n 'index-finger-phalanx-distal',\n 'index-finger-tip',\n 'middle-finger-metacarpal',\n 'middle-finger-phalanx-proximal',\n 'middle-finger-phalanx-intermediate',\n 'middle-finger-phalanx-distal',\n 'middle-finger-tip',\n 'ring-finger-metacarpal',\n 'ring-finger-phalanx-proximal',\n 'ring-finger-phalanx-intermediate',\n 'ring-finger-phalanx-distal',\n 'ring-finger-tip',\n 'pinky-finger-metacarpal',\n 'pinky-finger-phalanx-proximal',\n 'pinky-finger-phalanx-intermediate',\n 'pinky-finger-phalanx-distal',\n 'pinky-finger-tip',\n}\n\nexport const importantBits = [\n 'thumb-tip',\n 'index-finger-tip',\n 'middle-finger-tip',\n 'ring-finger-tip',\n 'pinky-finger-tip',\n]\n\nexport interface IJointGroup extends Group {\n handedness: 'left' | 'right'\n joints: { [key: string]: Object3D }\n}\n","import {\n left_grab,\n left_use,\n right_grab,\n right_use,\n} from 'src/controller/controls'\nimport { IJointGroup, vr_keys } from 'src/input/joints'\nimport { renderer } from 'src/render'\nimport { tick } from 'src/shader/time'\nimport { Value } from 'src/value'\nimport { Group, Object3D, Vector3 } from 'three'\nimport { mouse_left, mouse_pos, mouse_right } from './mouse'\n\nexport const left = [\n 0.18681570887565613, 1.382739543914795, -0.18258269131183624,\n 0.16145765781402588, 1.4247382879257202, -0.19330185651779175,\n 0.14029449224472046, 1.4471595287322998, -0.203621506690979,\n 0.11854463070631027, 1.465911626815796, -0.2214338481426239,\n 0.10323526710271835, 1.4838736057281494, -0.22844798862934113,\n 0.17240598797798157, 1.4193986654281616, -0.20432732999324799,\n 0.16084901988506317, 1.4546279907226562, -0.24567970633506775,\n 0.14093229174613953, 1.4714457988739014, -0.27322903275489807,\n 0.11823146790266037, 1.47991144657135, -0.27514559030532837,\n 0.09668971598148346, 1.4859790802001953, -0.2757745385169983,\n 0.16766689717769623, 1.4088687896728516, -0.20943443477153778,\n 0.15686826407909393, 1.4355288743972778, -0.2565702795982361,\n 0.1320498138666153, 1.4512417316436768, -0.2878733277320862,\n 0.10626484453678131, 1.4606534242630005, -0.29022565484046936,\n 0.08283071964979172, 1.4681520462036133, -0.2858382761478424,\n 0.16418185830116272, 1.39774751663208, -0.2118483930826187,\n 0.14816531538963318, 1.4166510105133057, -0.2572179138660431,\n 0.1123814508318901, 1.4268888235092163, -0.2688540816307068,\n 0.08852753043174744, 1.4348570108413696, -0.26027241349220276,\n 0.07257404923439026, 1.4425143003463745, -0.2435014396905899,\n 0.15973716974258423, 1.383512258529663, -0.21490654349327087,\n 0.13842615485191345, 1.3976171016693115, -0.2527333199977875,\n 0.11209113895893097, 1.4084179401397705, -0.2411767989397049,\n 0.10026412457227707, 1.416111707687378, -0.2265658974647522,\n 0.09213629364967346, 1.427158236503601, -0.2094181627035141,\n]\n\nexport const right = [\n -0.20063939690589905, 1.4035980701446533, -0.146737739443779,\n -0.17396438121795654, 1.4444682598114014, -0.1582299917936325,\n -0.14858832955360413, 1.4629813432693481, -0.16662096977233887,\n -0.12367366254329681, 1.4805759191513062, -0.1811697781085968,\n -0.10531037300825119, 1.496011734008789, -0.18671375513076782,\n -0.1867467612028122, 1.4405176639556885, -0.16837789118289948,\n -0.17614777386188507, 1.4762307405471802, -0.20957161486148834,\n -0.16556094586849213, 1.496448040008545, -0.23986446857452393,\n -0.14330923557281494, 1.5062041282653809, -0.2404555231332779,\n -0.1224694475531578, 1.5141875743865967, -0.24225161969661713,\n -0.18211762607097626, 1.430051326751709, -0.1737116575241089,\n -0.17240199446678162, 1.4572616815567017, -0.22076866030693054,\n -0.14676563441753387, 1.4754499197006226, -0.2500038146972656,\n -0.1224643811583519, 1.486384391784668, -0.2569943368434906,\n -0.0991157591342926, 1.4952983856201172, -0.25685617327690125,\n -0.17868107557296753, 1.4189616441726685, -0.17633163928985596,\n -0.16370420157909393, 1.4384002685546875, -0.22183001041412354,\n -0.13368573784828186, 1.4506241083145142, -0.2435133308172226,\n -0.10905411839485168, 1.4599230289459229, -0.23991341888904572,\n -0.09051994234323502, 1.4681507349014282, -0.22637824714183807,\n -0.17429772019386292, 1.4047664403915405, -0.17965318262577057,\n -0.15385453402996063, 1.419324517250061, -0.21778598427772522,\n -0.12518753111362457, 1.4303231239318848, -0.21877425909042358,\n -0.1070295199751854, 1.437195897102356, -0.21280734241008759,\n -0.08862950652837753, 1.4469671249389648, -0.20587250590324402,\n]\n\nconst $vec3 = new Vector3()\n\nexport const left_controller = new Value()\nexport const right_controller = new Value()\n\nexport class Phony extends Group implements IJointGroup {\n handedness: 'left' | 'right' = 'left'\n handData: number[]\n\n joints: { [key: string]: Group }\n lastLeft = new Vector3()\n lastRight = new Vector3()\n\n controllerOffset = new Vector3()\n\n constructor(handData: number[], handedness: 'left' | 'right' = 'left') {\n super()\n this.handData = handData\n this.handedness = handedness\n\n this.joints = {}\n\n for (let i = 0; i < handData.length / 3; i++) {\n const hand = new Group()\n this.joints[vr_keys[i]] = hand\n this.add(hand)\n }\n\n tick.on(this.tick.bind(this))\n // reset controller after XR done\n renderer.xr.addEventListener('sessionend', () => {\n this.controllerOffset.set(0, 0, 0)\n })\n }\n\n tick(t) {\n // determine controller position\n let controller\n switch (this.handedness) {\n case 'left':\n if (!left_controller.$) break\n this.controllerOffset.copy(left_controller.$.position)\n controller = left_controller.$\n break\n case 'right':\n if (!right_controller.$) break\n this.controllerOffset.copy(right_controller.$.position)\n controller = right_controller.$\n break\n }\n\n for (let i = 0; i < this.handData.length / 3; i++) {\n const hand = this.joints[vr_keys[i]]\n const forward =\n this.handedness !== 'left'\n ? mouse_left.$\n ? -1\n : 0\n : mouse_right.$\n ? -1\n : 0\n\n hand.position.set(\n this.handData[i * 3],\n this.handData[i * 3 + 1] - 1.6,\n this.handData[i * 3 + 2]\n )\n\n switch (true) {\n case this.handedness === 'left' && right_use.$:\n if (\n vr_keys[i].indexOf('thumb') !== -1 ||\n vr_keys[i].indexOf('index') !== -1\n )\n hand.position.x *= 0.9\n break\n case this.handedness === 'right' && left_use.$:\n if (\n vr_keys[i].indexOf('thumb') !== -1 ||\n vr_keys[i].indexOf('index') !== -1\n )\n hand.position.x *= 0.9\n break\n case this.handedness === 'right' && left_grab.$:\n hand.position.multiplyScalar(0.6)\n break\n case this.handedness === 'left' && right_grab.$:\n hand.position.multiplyScalar(0.6)\n break\n }\n\n // controller is moving\n if (this.controllerOffset.length() > 0) {\n hand.position\n .applyQuaternion(controller.quaternion)\n .add(this.controllerOffset)\n continue\n }\n\n // move hand left or right\n hand.position.x +=\n (this.handedness === 'left' ? 0.1 : -0.1) +\n Math.cos(t * 0.01) * 0.01 * (this.handedness === 'left' ? 1 : -1)\n\n hand.position.y += Math.sin(t * 0.01) * 0.01\n hand.position.z += -0.15\n\n let effect = 0.01\n\n switch (true) {\n case this.handedness === 'left' && mouse_pos.$.x > 0.4:\n effect = 0.075\n\n break\n case this.handedness !== 'left' && mouse_pos.$.x < -0.4:\n effect = 0.075\n break\n\n case this.handedness === 'left' && mouse_pos.$.x > -0.65:\n effect = 0.055\n\n break\n case this.handedness !== 'left' && mouse_pos.$.x < 0.65:\n effect = 0.055\n break\n }\n\n $vec3.set(\n mouse_pos.$.x * 2 * (forward !== 0 ? 2 : 1),\n mouse_pos.$.y * 3 * (forward !== 0 ? 2 : 1),\n forward * 2\n )\n\n $vec3.z *= 2 * effect\n $vec3.x *= effect * 2\n $vec3.y *= effect * 2\n\n const target = this.handedness === 'left' ? this.lastLeft : this.lastRight\n target.multiplyScalar(4).add($vec3).divideScalar(5)\n\n hand.position.add(target)\n // lerp towards mouse a little\n }\n }\n}\n","export interface IAtomic {\n sab: SharedArrayBuffer\n\n store(index: number, value: number): number\n load(index: number): number\n free(index: number)\n}\n\nconst strConvertBuffer = new ArrayBuffer(4) // an Int32 takes 4 bytes\nconst strView = new DataView(strConvertBuffer)\nfunction CharCode(code: number) {\n switch (code) {\n case 0:\n return ''\n default:\n return String.fromCharCode(code)\n }\n}\n\nexport function StringToInt(str: string) {\n // max 4 chars\n str = str.slice(0, 4)\n\n for (let si = 0; si < 4; si++) {\n if (si < str.length) {\n strView.setUint8(si, str.charCodeAt(si))\n } else {\n strView.setUint8(si, 0)\n }\n }\n\n return strView.getInt32(0)\n}\n\nexport function IntToString(num: number) {\n strView.setInt32(0, num, false)\n return (\n CharCode(strView.getUint8(0)) +\n CharCode(strView.getUint8(1)) +\n CharCode(strView.getUint8(2)) +\n CharCode(strView.getUint8(3))\n )\n}\n\nexport class AtomicInt extends Int32Array implements IAtomic {\n sab: SharedArrayBuffer\n constructor(sab: SharedArrayBuffer) {\n super(sab)\n this.sab = sab\n }\n\n store(index: number, value: number): number {\n return Atomics.store(this, index, value)\n }\n\n load(index: number): number {\n return Atomics.load(this, index)\n }\n\n free(index: number, size: number = 1) {\n for (let i = 0; i < size; i++) {\n Atomics.store(this, index * size + i, 0)\n }\n }\n}\n\nexport class AtomicByte extends Uint8Array implements IAtomic {\n sab: SharedArrayBuffer\n constructor(sab: SharedArrayBuffer) {\n super(sab)\n this.sab = sab\n }\n\n store(index: number, value: number): number {\n return Atomics.store(this, index, value)\n }\n\n load(index: number): number {\n return Atomics.load(this, index)\n }\n\n free(index: number, size: number = 1) {\n for (let i = 0; i < size; i++) {\n Atomics.store(this, index * size + i, 0)\n }\n }\n}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\n\nexport enum EAnimation {\n NORMAL = 0,\n OFF,\n FIRE,\n DISCARD,\n GATE,\n}\n\nexport class Animation extends AtomicInt {\n static COUNT = 1\n\n constructor(\n buffer = new SharedArrayBuffer(ATOM_COUNT * Animation.COUNT * 4)\n ) {\n super(buffer)\n }\n\n animation(i: number, animation?: EAnimation): EAnimation {\n return animation === undefined\n ? Atomics.load(this, Animation.COUNT * i)\n : Atomics.store(this, Animation.COUNT * i, animation)\n }\n}\n","import { AtomicInt, IntToString, StringToInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\n\n// how together something is\nexport enum EPhase {\n // doesn't get added to any collision\n GHOST,\n // doesn't exist according to physics\n VOID,\n // levels of reactivity\n LIQUID,\n // Doesn't move, but exists\n STUCK,\n FLOAT,\n DIVINE,\n}\n\nexport enum ECarries {\n LEFT_HAND = -1,\n RIGHT_HAND = -2,\n}\n\nexport class Phys extends AtomicInt {\n static COUNT = 8\n\n constructor(shared = new SharedArrayBuffer(ATOM_COUNT * Phys.COUNT * 4)) {\n super(shared)\n }\n\n phase(i: number, p?: EPhase) {\n return p !== undefined\n ? Atomics.store(this, i * Phys.COUNT, p)\n : Atomics.load(this, i * Phys.COUNT)\n }\n\n core(i: number, g?: number) {\n return g !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 1, g)\n : Atomics.load(this, i * Phys.COUNT + 1)\n }\n\n carried(i: number, c?: number | ECarries) {\n return c !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 2, c)\n : Atomics.load(this, i * Phys.COUNT + 2)\n }\n\n carries(i: number, c?: number) {\n return c !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 3, c)\n : Atomics.load(this, i * Phys.COUNT + 3)\n }\n\n distance(i: number, d?: number) {\n return d !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 4, d)\n : Atomics.load(this, i * Phys.COUNT + 4)\n }\n\n tag(i: number, f?: string) {\n return f !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 5, StringToInt(f))\n : IntToString(Atomics.load(this, i * Phys.COUNT + 5))\n }\n\n tag2(i: number, f?: string) {\n return f !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 6, StringToInt(f))\n : IntToString(Atomics.load(this, i * Phys.COUNT + 6))\n }\n\n spell(i: number, s?: number) {\n return s !== undefined\n ? Atomics.store(this, i * Phys.COUNT + 7, s)\n : Atomics.load(this, i * Phys.COUNT + 7)\n }\n}\n","import { AtomicByte } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\n\nexport enum EStatus {\n Unassigned = 0,\n Assigned,\n}\n\nexport enum ERole {\n NONE = 0,\n HUNTER,\n SEEKER,\n}\n\nexport class Traits extends AtomicByte {\n static COUNT = 2\n\n constructor(buffer = new SharedArrayBuffer(ATOM_COUNT * Traits.COUNT)) {\n super(buffer)\n }\n\n status(i: number, status?: EStatus): EStatus {\n return status === undefined\n ? Atomics.load(this, i * Traits.COUNT)\n : Atomics.store(this, i * Traits.COUNT, status)\n }\n\n role(i: number, role?: ERole): ERole {\n return role === undefined\n ? Atomics.load(this, i * Traits.COUNT + 1)\n : Atomics.store(this, i * Traits.COUNT + 1, role)\n }\n}\n","import { EAnimation } from 'src/buffer/animation'\nimport { EPhase } from 'src/buffer/phys'\nimport { ERole } from 'src/buffer/traits'\n\nexport enum EWhen {\n NEVER,\n IMMEDIATELY,\n HIT,\n REZ,\n}\nexport interface IMarkers {\n [markerID: number]: string\n}\n\nexport interface INode {\n $?: number[]\n _: { [key: string]: INode }\n}\n\n// easier for humans to read\nexport interface IFate extends INode {\n markers: IMarkers\n flat: { [key: number]: INode }\n}\n\nexport enum ETomeRipple {\n RIPPLE = 0,\n SINK,\n}\nexport enum ETomeLive {\n LIVE = 0,\n ETHEREAL,\n}\n\nexport enum EImpactReaction {\n NONE = 0,\n DESTROY,\n BOUNCE,\n DESTROY_OTHER,\n DESTROY_BOTH,\n RESPAWN,\n GATE,\n}\n\nexport enum EShape {\n PLANE,\n RING,\n RECT,\n LINE,\n // Sphere,\n // Circle,\n\n // Wall,\n // Box,\n}\n\nexport enum ESelectThen {\n NOTHING = 0,\n FREE,\n // APPLY,\n // COPY,\n}\n\nexport enum EShapePattern {\n NONE = -1,\n EDGES = 0,\n DIAGONAL,\n DIAGONAL_TIGHT,\n DIAGONAL_SLOPPY,\n MISC_BARS,\n DIAGONAL_PLAID,\n WAVES,\n WAVES_TIGHT,\n DIAGONAL_SQUARE,\n MISC_UPVOTE,\n SNAKE,\n SNAKE_CHANNEL,\n DIAGONAL_LOOSE,\n EDGES_BARRED,\n CHECKER,\n CHECKER_TIGHT,\n BRICK,\n BRICK_TIGHT,\n BRICK_TIGHT_ROUND,\n BRICK_SLOPPY,\n BRICK_TIGHT_SLOPPY,\n BRICK_VERTICAL,\n TILE_CIRCLE,\n CANDY_CORN,\n DIAGONAL_WAVE,\n DIAGONAL_WAVE_SLOPPY,\n DIAGONAL_WAVE_TIGHT,\n WAVE,\n WAVE_TIGHT,\n WAVE_PLAID,\n WAVE_INVERT,\n TILE_ORGANIC,\n TILE_DIAGONAL_CIRCUIT,\n STAR_TIGHT,\n CHECKER_TRIANGLES,\n CHECKER_SQUARE_TILT,\n STONE_HEX,\n STONE_ROUND,\n STONE_ROUND_ALT,\n STONE_TILE,\n STONE_LOOSE,\n STONE_DIAGONAL,\n TRIANGLES,\n TRIANGLES_ALT,\n TRIANGLES_TIGHT,\n STAR_LOOSE,\n PIPE_STRIPE,\n CROSS,\n MISC_LAYERED,\n TILE_BOARDS,\n DIAMONDS_SLOPPY,\n SQUARES_TILT,\n SQUARES_TILT_ALT,\n SQUARES_TILT_LOOSE,\n SQUARES_SPARSE,\n MISC_PETALS_SPARSE,\n HEX_LOOSE,\n HEX_TIGHT,\n TILE_DISTRIBUTED,\n CROSS_TIGHT,\n DIAMONDS,\n DIAMONDS_LOOSE,\n DIAMONDS_SPARSE,\n MISC_PENTAGON,\n DIAMONDS_TIGHT,\n DIAMONDS_TIGHT_SLOPPY,\n BRICK_SPRASE_SLOPPY,\n BRICK_SPARSE_SLOPPY_ALT,\n CIRCLE_SLOPPY,\n CIRCLE,\n CIRCLE_TIGHT,\n MISC_HONEYCOMB,\n DOTS,\n DOTS_TIGHT,\n TILE_HEARTS,\n TILE_SHIELDS,\n TILE_ORGANIC_TIGHT,\n TILE_DIAMOND,\n WATER,\n WATER_ALT,\n STONE,\n STONE_ALT,\n MISC_ZEBRA,\n TILE_ZIPPER,\n}\n\nexport enum EAxis {\n X,\n Y,\n Z,\n XY,\n YZ,\n XZ,\n XYZ,\n}\n\n// Only allows 0.01 precision\nexport enum EVar {\n NUMBER,\n STRING,\n POSITIVE,\n NEGATIVE,\n // allows for selection of ID by marker name\n TOME,\n COLOR,\n VOX,\n // think 0 - 1 but like 0 - MAX_SAFE_INTEGER\n NORMAL,\n NOISE,\n // -1 0 1\n SIGN,\n TIME,\n FAENUMBER,\n FAEPOSITIVE,\n FIVEFINGERS,\n BOOL,\n LONGSTRING,\n SHORTSTRING,\n PATTERN,\n}\n\nexport enum ECarriers {\n NONE,\n HAND_LEFT,\n HAND_RIGHT,\n HAND_BETWEEN,\n}\n\nexport enum EIdle {\n RANDOMIZE,\n NONE,\n}\n\nexport enum EGameStatus {\n NOT_STARTED,\n INIT,\n PLAYING,\n WIN,\n LOST,\n DRAW,\n END,\n}\n\n// ETimeline events are reversable transactions that allow for time travel\n// WARNING: Safe to add new events to end but not remove/reorder existing ones\nexport enum ESpell {\n NONE = 0,\n TOME,\n // unused\n MUSIC,\n // deprecated\n FLOCK,\n SHAPE_COLOR,\n SHAPE,\n SHAPE_VAR,\n DO_REZ,\n DO_FREE,\n POS,\n POS_TO,\n POS_VAR,\n THRUST,\n THRUST_TO,\n THRUST_VAR,\n ROT,\n ROT_LOOK,\n ROT_LOOK_TO,\n\n // When something interesting happens the who\n EVENT,\n\n // Cage this physics item to these bounds\n PHYS_CAGE,\n PHYS_PHASE,\n // TODO:\n CONTROL,\n NET,\n AI,\n SHAPE_EFFECTS,\n VOX,\n ROT_VAR,\n PHYS_IMPACT,\n VOX_VAR,\n FAE_POS,\n FAE_ROT,\n FAE_SIZE,\n UNI_IDLE,\n FLOCK_TEXT,\n UNI_CLEAR_COLOR,\n POS_ADD,\n THRUST_ADD,\n THEIA_REALM,\n REACT_GATE,\n THEIA_RULER,\n SOUND_MIDI,\n SOUND,\n SOUND_POS,\n REZ_POSE,\n TRIGGER,\n FLOCK_RING,\n FLOCK_GRID,\n FAE_AVATAR,\n VOX_BREAK,\n FLOCK_RECT,\n GAME_SCORE,\n MIDI,\n GAME_STATUS,\n DO_SELECT,\n // free\n TRAP_DISTANCE,\n TRAP_FILTER,\n TRAP_TIME,\n TRAP_CLEAR,\n // end free\n MIDI_INSTRUMENT,\n DO_SEEK,\n UNI_GRAVITY,\n FAE_COLOR,\n MIDI_CHIRP,\n PHYS_CARRIED,\n TOME_OPTIONS,\n NOISE_PASSIVE,\n // soon\n ___IF,\n ___VAR,\n FLOCK_LINE,\n NOISE_HURT,\n NOISE_ACTIVE,\n NOISE_LOVE,\n NOISE_DEATH,\n ATTACH,\n SHAPE_PATTERN,\n}\n\nexport enum EConstraint {\n FULL = 0,\n NEGATIVE = -1,\n POSITIVE = 1,\n}\n\nexport const ESpellHelp = {\n [ESpell.TOME]: 'A holder of knowledge',\n [ESpell.FLOCK_GRID]: 'A flock of atoms set in a uniform grid',\n [ESpell.FLOCK_RING]: 'A flock of atoms set in a ring',\n [ESpell.FLOCK_TEXT]: 'A flock of atoms set as text',\n [ESpell.SHAPE_COLOR]: 'Atomic color',\n [ESpell.SHAPE]: 'Atomic Shape/Size',\n [ESpell.SHAPE_VAR]: 'Atomic Shape/Size Variance',\n [ESpell.DO_REZ]: 'Rez a Tome',\n [ESpell.DO_FREE]: 'Free all atoms in rezzed by this Tome',\n [ESpell.POS]: 'Atomic Position',\n [ESpell.POS_TO]: 'Atomic Position to',\n [ESpell.POS_VAR]: 'Atomic Position Variance',\n [ESpell.THRUST]: 'Atomic Thrust',\n [ESpell.THRUST_TO]: 'Atomic Thrust to',\n [ESpell.THRUST_VAR]: 'Atomic Thrust Variance',\n [ESpell.ROT]: 'Atomic Rotation',\n [ESpell.ROT_LOOK]: 'Atomic Rotation to look at',\n [ESpell.ROT_LOOK_TO]: 'Atomic Rotation to look at to',\n [ESpell.ROT_VAR]: 'Atomic Rotation Variance',\n [ESpell.THEIA_RULER]:\n 'SET THE THEIA RULER FOR LOADING FROM GITHUB. MAPS TO GITHUB FAE NAME.',\n [ESpell.THEIA_REALM]: 'SET THE THEIA REALM, MAPS TO FILE NAME WITHOUT .FATE',\n [ESpell.EVENT]: 'An event',\n [ESpell.PHYS_CAGE]: 'Cage atoms to these bounds, also sets realm shader cage',\n [ESpell.PHYS_PHASE]: 'Atomic Phase for physics',\n [ESpell.AI]: 'Atomic AI routine. HUNTER hunts the avatar',\n [ESpell.SHAPE_EFFECTS]: 'Atomic Shader Effects, like NOEFFECTS',\n [ESpell.VOX]:\n 'Atomic Voxel Shape. Forms a pattern at the point of a flock based on a .vox file.',\n [ESpell.VOX_VAR]: 'Atomic Voxel Shape Variance of position within the vox',\n [ESpell.FAE_AVATAR]: 'Atomic Fae Avatar designation',\n [ESpell.FAE_POS]: 'Atomic Fae Position',\n [ESpell.FAE_ROT]: 'Atomic Fae Rotation',\n [ESpell.FAE_SIZE]: 'Atomic Fae Size',\n [ESpell.UNI_IDLE]:\n 'Atomic Idle routine. IDLE_RANDOMIZE randomizes the idle routine',\n [ESpell.UNI_CLEAR_COLOR]: 'Clear Color',\n [ESpell.DO_SEEK]: 'Seek to a position in the track without causing a reset',\n [ESpell.PHYS_CARRIED]: 'Use the targets position as the base position',\n [ESpell.TOME_OPTIONS]: 'Turn on/off the ripple and live tome options',\n\n [ESpell.___VAR]: 'Set a variable [name] [action] [with]',\n [ESpell.___IF]:\n 'If [blah] is [greater|less|equal| to [other blah] do these things',\n [ESpell.FLOCK_LINE]: 'A flock of atoms set in a line',\n}\n\nexport const Invocations: { [key: number]: any } = {\n [ESpell.TOME]: { text: EVar.STRING },\n\n [ESpell.PHYS_PHASE]: {\n phase: EPhase,\n tag: EVar.SHORTSTRING,\n tag2: EVar.SHORTSTRING,\n },\n [ESpell.SHAPE_COLOR]: {\n rgb: EVar.COLOR,\n tilt: EVar.NORMAL,\n variance: EVar.NORMAL,\n },\n [ESpell.VOX_VAR]: {\n rgb: EVar.COLOR,\n tilt: EVar.NORMAL,\n variance: EVar.NORMAL,\n },\n [ESpell.SHAPE]: {\n x: EVar.FAEPOSITIVE,\n y: EVar.FAEPOSITIVE,\n z: EVar.FAEPOSITIVE,\n },\n\n [ESpell.SHAPE_VAR]: {\n x: EVar.FAEPOSITIVE,\n y: EVar.FAEPOSITIVE,\n z: EVar.FAEPOSITIVE,\n },\n [ESpell.ROT]: { x: EVar.NORMAL, y: EVar.NORMAL, z: EVar.NORMAL },\n [ESpell.ROT_VAR]: { x: EVar.NORMAL, y: EVar.NORMAL, z: EVar.NORMAL },\n\n [ESpell.DO_REZ]: {\n count: EVar.NUMBER,\n },\n [ESpell.DO_FREE]: {},\n\n [ESpell.POS]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n [ESpell.POS_VAR]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n [ESpell.THRUST]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n\n [ESpell.THRUST_VAR]: {\n axis: EAxis,\n thrust: EVar.FAEPOSITIVE,\n constraint: EConstraint,\n },\n\n [ESpell.ROT_LOOK]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n\n [ESpell.SHAPE_EFFECTS]: { animation: EAnimation },\n [ESpell.VOX]: { 'Vox Model': EVar.VOX },\n [ESpell.PHYS_IMPACT]: {\n reaction: EImpactReaction,\n is: EVar.SHORTSTRING,\n isNot: EVar.SHORTSTRING,\n },\n [ESpell.PHYS_CAGE]: {\n axis: EAxis,\n min: EVar.FAENUMBER,\n max: EVar.FAENUMBER,\n },\n\n [ESpell.UNI_IDLE]: { idle: EIdle },\n [ESpell.FAE_POS]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n\n [ESpell.FAE_SIZE]: { size: EVar.FAEPOSITIVE },\n [ESpell.FLOCK_TEXT]: { text: EVar.STRING },\n [ESpell.UNI_CLEAR_COLOR]: { rgb: EVar.COLOR },\n [ESpell.FAE_ROT]: { x: EVar.NORMAL, y: EVar.NORMAL, z: EVar.NORMAL },\n [ESpell.POS_ADD]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n [ESpell.THRUST_ADD]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n\n [ESpell.THEIA_RULER]: { githubFae: EVar.STRING },\n [ESpell.THEIA_REALM]: { theia: EVar.STRING },\n [ESpell.REACT_GATE]: { theia: EVar.STRING },\n\n [ESpell.FLOCK_RING]: {\n radius: EVar.FAEPOSITIVE,\n count: EVar.POSITIVE,\n },\n [ESpell.FLOCK_GRID]: {\n birds_squared: EVar.POSITIVE,\n margin: EVar.FAEPOSITIVE,\n birds_length_optional: EVar.POSITIVE,\n },\n\n [ESpell.FAE_AVATAR]: {\n thrustStrength: EVar.FAENUMBER,\n },\n\n [ESpell.AI]: {\n role: ERole,\n },\n [ESpell.FLOCK_RECT]: {\n x: EVar.POSITIVE,\n z: EVar.POSITIVE,\n margin: EVar.FAEPOSITIVE,\n },\n\n [ESpell.GAME_SCORE]: {\n score: EVar.NUMBER,\n },\n\n [ESpell.GAME_STATUS]: {\n game_status: EGameStatus,\n },\n\n [ESpell.VOX_BREAK]: {\n Percentage: EVar.NORMAL,\n },\n [ESpell.DO_SEEK]: {\n when: EVar.TIME,\n },\n [ESpell.UNI_GRAVITY]: {\n x: EVar.FAENUMBER,\n y: EVar.FAENUMBER,\n z: EVar.FAENUMBER,\n },\n [ESpell.FAE_COLOR]: {\n color: EVar.COLOR,\n hueVariance: EVar.NORMAL,\n },\n\n [ESpell.TOME_OPTIONS]: {\n ripple: ETomeRipple,\n liveliness: ETomeLive,\n },\n\n [ESpell.FLOCK_LINE]: {\n count: EVar.POSITIVE,\n spacing: EVar.FAEPOSITIVE,\n direction: EAxis,\n },\n\n [ESpell.SHAPE_PATTERN]: {\n pattern: EVar.PATTERN,\n },\n [ESpell.DO_SELECT]: {\n then: ESelectThen,\n is: EVar.SHORTSTRING,\n not: EVar.SHORTSTRING,\n },\n}\n","import { ATOM_COUNT } from 'src/config'\nimport { Box3 } from 'three'\nimport { AtomicInt } from './atomic'\n\nexport class Cage extends AtomicInt {\n static COUNT = 6\n\n constructor(buffer = new SharedArrayBuffer(ATOM_COUNT * Cage.COUNT * 4)) {\n super(buffer)\n }\n addX(i: number, x: number) {\n return Atomics.add(this, i * Cage.COUNT, x)\n }\n addY(i: number, y: number) {\n return Atomics.add(this, i * Cage.COUNT + 1, y)\n }\n\n addZ(i: number, z: number) {\n return Atomics.add(this, i * Cage.COUNT + 2, z)\n }\n\n addMX(i: number, x: number) {\n return Atomics.add(this, i * Cage.COUNT + 3, x)\n }\n\n addMY(i: number, y: number) {\n return Atomics.add(this, i * Cage.COUNT + 4, y)\n }\n\n addMZ(i: number, z: number) {\n return Atomics.add(this, i * Cage.COUNT + 5, z)\n }\n\n x(i: number, x?: number) {\n return x === undefined\n ? Atomics.load(this, i * Cage.COUNT)\n : Atomics.store(this, i * Cage.COUNT, x)\n }\n\n y(i: number, y?: number) {\n return y === undefined\n ? Atomics.load(this, i * Cage.COUNT + 1)\n : Atomics.store(this, i * Cage.COUNT + 1, y)\n }\n\n z(i: number, z?: number) {\n return z === undefined\n ? Atomics.load(this, i * Cage.COUNT + 2)\n : Atomics.store(this, i * Cage.COUNT + 2, z)\n }\n\n mX(i: number, x?: number) {\n return x === undefined\n ? Atomics.load(this, i * Cage.COUNT + 3)\n : Atomics.store(this, i * Cage.COUNT + 3, x)\n }\n\n mY(i: number, y?: number) {\n return y === undefined\n ? Atomics.load(this, i * Cage.COUNT + 4)\n : Atomics.store(this, i * Cage.COUNT + 4, y)\n }\n\n mZ(i: number, z?: number) {\n return z === undefined\n ? Atomics.load(this, i * Cage.COUNT + 5)\n : Atomics.store(this, i * Cage.COUNT + 5, z)\n }\n\n box(i, box: Box3) {\n this.x(i, box.min.x)\n this.y(i, box.min.y)\n this.z(i, box.min.z)\n this.mX(i, box.max.x)\n this.mY(i, box.max.y)\n this.mZ(i, box.max.z)\n }\n}\n","import { AtomicInt, IntToString, StringToInt } from 'src/buffer/atomic'\nimport { NORMALIZER, TIMELINE_MAX } from 'src/config'\nimport {\n EShapePattern,\n ESpell,\n EVar,\n IFate,\n INode,\n Invocations,\n} from 'src/fate/weave'\nimport { Color } from 'three'\n\nconst strConvertBuffer = new ArrayBuffer(4) // an Int32 takes 4 bytes\nconst strView = new DataView(strConvertBuffer)\n\nconst $color = new Color()\n\nfunction CharCode(code: number) {\n switch (code) {\n case 0:\n return ''\n default:\n return String.fromCharCode(code)\n }\n}\n\n// 60^n\nconst timeEscalation = [1, 60, 60 * 60]\nfunction FromDateString(str: string) {\n return str\n .split(':')\n .reverse()\n .reduce((s, t, i) => {\n return s + parseInt(t, 10) * timeEscalation[i]\n }, 0)\n}\nconst viewer = new DataView(new ArrayBuffer(4))\n\nfunction ToDateString(seconds: number) {\n const s = seconds % 60\n const h = Math.floor(seconds / (60 * 60))\n const m = Math.floor(seconds / 60) % 60\n return `${h > 0 ? `${h}:` : ``}${m > 0 ? `${m}:` : ``}${s}`\n}\n// Authoring Buffer - Generally shouldn't be updating the timeline unless during development\nexport class Fate extends AtomicInt {\n static COUNT = 6\n available: number[]\n\n // expandable\n constructor(sab = new SharedArrayBuffer(Fate.COUNT * 4 * TIMELINE_MAX)) {\n super(sab)\n // reset available\n this.available = [...new Array(TIMELINE_MAX)].map((_, i) => i)\n // never 0\n this.reserve()\n }\n\n copy(arr: ArrayBuffer) {\n this.freeAll()\n this.resetAvailable()\n\n const v = new DataView(arr)\n\n for (\n let i = 0;\n i < Math.min(arr.byteLength / 4, this.sab.byteLength / 4);\n i++\n ) {\n const val = v.getInt32(i * 4, true)\n\n if (i % Fate.COUNT === 0 && val !== 0) {\n this.available.splice(this.available.indexOf(i / Fate.COUNT), 1)\n }\n this.store(i, val)\n }\n }\n // rip through array and create a tree\n toObject(): IFate {\n // cache markers by number\n\n const root = {\n flat: {},\n markers: {},\n $: [\n this.when(0),\n this.spell(0),\n this.who(0),\n this.data0(0),\n this.data1(0),\n this.data2(0),\n ],\n _: {},\n }\n\n for (let i = 1; i < TIMELINE_MAX; i++) {\n const com = this.spell(i)\n\n // next, the others could be anywhere\n if (com === ESpell.NONE) continue\n\n // assume root unless who is specified\n const who = this.who(i)\n\n let cursor\n switch (true) {\n case who === 0:\n cursor = root\n break\n\n case root.flat[who] !== undefined:\n cursor = root.flat[who]\n break\n\n default:\n root.flat[who] = cursor = {\n _: {},\n }\n }\n\n switch (com) {\n case ESpell.TOME:\n root.markers[i] = this.text(i)\n\n // fall through\n default:\n if (!root.flat[i]) {\n root.flat[i] = {\n _: {},\n }\n }\n\n root.flat[i].$ = [\n this.when(i),\n com,\n who,\n this.data0(i),\n this.data1(i),\n this.data2(i),\n ]\n\n cursor._[i] = root.flat[i]\n }\n }\n\n return root\n }\n\n toScript(): string {\n const obj = this.toObject()\n\n let output = '# You have encountered Fate! \\n# Load on vox.run\\n\\n'\n\n const addChild = (target: INode, t = 0) => {\n const addT = () => [...new Array(t)].map(() => '\\t').join('')\n // render\n for (let child of Object.entries(target._)) {\n // get commmand\n const [k, v] = child\n const [when, comm] = v.$\n\n output += `\\n${addT()}(`\n\n const invoke = Invocations[comm]\n\n if (invoke !== ESpell.TOME && when !== 0) {\n output += `${ToDateString(when)} `\n }\n\n output += `${ESpell[comm].toLowerCase()} `\n\n if (invoke) {\n // check for data\n const evarInfo = Object.values(invoke)\n for (let i = 0; i < evarInfo.length; i++) {\n const e = evarInfo[i]\n const d = v.$[3 + i]\n\n switch (e) {\n case EVar.NOISE:\n viewer.setInt32(0, d)\n output += `0x${[...new Array(4)]\n .map((_, i) =>\n `00${viewer.getUint8(i).toString(16)}`.slice(-2)\n )\n .join('')} `\n break\n case EVar.TIME:\n output += `${ToDateString(d)} `\n break\n case EVar.BOOL:\n output += `${d ? 'true' : 'false'} `\n break\n case EVar.SHORTSTRING:\n output += `\"${IntToString(d)}\" `\n break\n case EVar.VOX:\n case EVar.STRING:\n output += `\"${this.text(parseInt(k, 10))}\" `\n break\n case EVar.COLOR:\n output += `0x${$color.set(d).getHexString()} `\n break\n case EVar.NORMAL:\n output += `${d / NORMALIZER} `\n break\n case EVar.PATTERN:\n output += `${EShapePattern[d]} `\n break\n case EVar.FAENUMBER:\n case EVar.FAEPOSITIVE:\n output += `${d * 0.01} `\n break\n case EVar.NEGATIVE:\n case EVar.POSITIVE:\n case EVar.NUMBER:\n output += `${d} `\n break\n\n default:\n // probably an enum\n output += `${e[d]} `\n }\n }\n }\n // children\n if (Object.keys(v._).length > 0) {\n addChild(v, t + 1)\n output += `\\n${addT()}`\n }\n\n output += `)`\n }\n }\n\n addChild(obj)\n\n output += '\\n'\n return output\n }\n\n fromScript(name: string, script: string) {\n this.freeAll()\n\n script = script.replace(/[\\n\\t]/g, '')\n\n const RBLOB = /\\(.*\\)/\n\n const commands = []\n const text = []\n\n let res = RBLOB.exec(script)\n if (res === null) return\n\n // just the commands now\n script = res[0]\n\n this.text(0, name)\n\n while ((res = /\\(([A-Za-z0-9!.?$#\\-+*/':\"_ ]*)\\)/g.exec(script))) {\n let txt\n let code = res[1]\n\n // pull out text blobs\n while ((txt = /['\"]([A-Za-z0-9 .+\\\\:\\-!?]*)['\"]/g.exec(code))) {\n code = splice(code, txt.index, txt[0].length, `$${text.length}`)\n text.push(txt[1])\n }\n\n commands.push(code)\n script = splice(script, res.index, res[0].length, `#${commands.length} `)\n }\n\n for (let command of commands) {\n try {\n const i = this.reserve()\n\n let spell: ESpell\n let d = 0\n\n let evoke\n let ks\n for (let item of command.split(' ')) {\n if (item.length === 0) continue\n if (spell === undefined) {\n // parse to see if this is a time\n const t = FromDateString(item)\n if (isNaN(t)) {\n // @ts-ignore\n spell = ESpell[item.toUpperCase()]\n\n this.spell(i, spell)\n evoke = Invocations[spell]\n ks = Object.values(evoke || {})\n } else {\n this.when(i, t)\n }\n\n continue\n }\n\n if (item[0] === '#') {\n this.who(parseInt(item.slice(1), 10), i)\n continue\n }\n\n // this is a data item\n if (!evoke || ks[d] === undefined) continue\n\n const dat = `data${d}`\n switch (ks[d]) {\n case EVar.NOISE:\n this[dat](i, parseInt(item, 16))\n break\n case EVar.TIME:\n this[dat](i, FromDateString(item))\n break\n case EVar.COLOR:\n this[dat](i, parseInt(item, 16))\n break\n case EVar.FAENUMBER:\n case EVar.FAEPOSITIVE:\n this[dat](i, Math.round(parseFloat(item) * 100))\n break\n case EVar.NORMAL:\n this[dat](i, parseFloat(item) * NORMALIZER)\n break\n case EVar.POSITIVE:\n case EVar.NEGATIVE:\n case EVar.NUMBER:\n this[dat](i, parseInt(item, 10))\n break\n case EVar.SHORTSTRING:\n this[dat](i, StringToInt(text[item.slice(1)]))\n break\n case EVar.STRING:\n case EVar.VOX:\n this.text(i, text[item.slice(1)])\n break\n\n case EVar.PATTERN:\n this[dat](i, EShapePattern[item])\n break\n\n case EVar.BOOL:\n this[dat](i, item === 'true')\n break\n default:\n // probably an enum\n this[dat](i, ks[d][item])\n }\n d++\n }\n } catch (e) {\n console.error('LISP', command, e)\n }\n }\n }\n\n toJSON(): string {\n const { _, $ } = this.toObject()\n return JSON.stringify({ _, $ }, undefined, ' ')\n }\n\n // rip through and return a sorted array of the events\n toArray() {\n const res = [[]]\n // 1 is for control\n for (let i = 1; i < this.length; i++) {\n const w = this.spell(i)\n\n // by using the buffer in order we can look to see if we can early exit\n if (w === ESpell.NONE) break\n\n res.push([\n this.when(i),\n w,\n this.who(i),\n this.data0(i),\n this.data1(i),\n this.data2(i),\n ])\n }\n\n return res.sort((e1, e2) => {\n return e1[0] - e2[0]\n })\n }\n\n fromArray(arr: number[][]) {\n // reset existing\n this.freeAll()\n\n // always skip 0\n for (let i = 1; i < arr.length; i++) {\n this.add.apply(this, arr[i])\n }\n\n this.resetAvailable(arr.length)\n }\n\n resetAvailable(offset: number = 0) {\n // reset available\n this.available = [...new Array(TIMELINE_MAX - offset)].map(\n (_, i) => i + offset\n )\n }\n\n // freeAll but do not mark them as available\n freeAll() {\n this.available = []\n\n for (let i = TIMELINE_MAX - 1; i >= 0; i--) {\n this.free(i)\n }\n // always reserve 0\n this.reserve()\n }\n\n reserve() {\n return this.available.shift()\n }\n\n free(i: number) {\n super.free(i, Fate.COUNT)\n this.available.unshift(i)\n }\n\n add(\n when: number,\n command: ESpell,\n // identification number, whether timeline ID or\n who: number,\n d1: number = 0,\n d2: number = 0,\n d3: number = 0\n ) {\n if (this.available.length === 0) {\n throw new Error('Timeline full')\n }\n\n const i = this.available.shift()\n\n Atomics.store(this, i * Fate.COUNT, when)\n Atomics.store(this, i * Fate.COUNT + 1, command)\n Atomics.store(this, i * Fate.COUNT + 2, who)\n Atomics.store(this, i * Fate.COUNT + 3, d1)\n Atomics.store(this, i * Fate.COUNT + 4, d2)\n Atomics.store(this, i * Fate.COUNT + 5, d3)\n\n return i\n }\n\n short(i: number, step: number, str?: string) {\n if (str === undefined) {\n const num = this[`data${step}`](i)\n\n strView.setInt32(0, num, false)\n return (\n CharCode(strView.getUint8(0)) +\n CharCode(strView.getUint8(1)) +\n CharCode(strView.getUint8(2)) +\n CharCode(strView.getUint8(3))\n )\n }\n\n // max 4 chars\n str = str.slice(0, 4)\n\n for (let si = 0; si < 4; si++) {\n if (si < str.length) {\n strView.setUint8(si, str.charCodeAt(si))\n } else {\n strView.setUint8(si, 0)\n }\n }\n Atomics.store(this, i * Fate.COUNT + step + 3, strView.getInt32(0, false))\n\n return str\n }\n\n // defines are special, only strings available\n text(i: number, str?: string) {\n if (str === undefined) {\n return [this.data0(i), this.data1(i), this.data2(i)]\n .map((num: number) => {\n strView.setInt32(0, num, false)\n return (\n CharCode(strView.getUint8(0)) +\n CharCode(strView.getUint8(1)) +\n CharCode(strView.getUint8(2)) +\n CharCode(strView.getUint8(3))\n )\n })\n .join('')\n }\n\n // max 12 chars\n str = str.slice(0, 12)\n\n for (let si = 0; si < 12; si++) {\n const six = si % 4\n const siy = Math.floor(si / 4)\n\n if (si < str.length) {\n strView.setUint8(six, str.charCodeAt(si))\n } else {\n strView.setUint8(six, 0)\n }\n\n // last\n if (six === 3) {\n Atomics.store(\n this,\n i * Fate.COUNT + 3 + siy,\n strView.getInt32(0, false)\n )\n }\n }\n\n return str\n }\n\n // when\n when(i: number, when?: number) {\n return when === undefined\n ? Atomics.load(this, i * Fate.COUNT)\n : Atomics.store(this, i * Fate.COUNT, when)\n }\n\n // what event\n spell(i: number, e?: ESpell) {\n return e === undefined\n ? Atomics.load(this, i * Fate.COUNT + 1)\n : Atomics.store(this, i * Fate.COUNT + 1, e)\n }\n\n // used for refering to something\n who(i: number, who?: number) {\n return who === undefined\n ? Atomics.load(this, i * Fate.COUNT + 2)\n : Atomics.store(this, i * Fate.COUNT + 2, who)\n }\n\n data0(i: number, d1?: number) {\n return d1 === undefined\n ? Atomics.load(this, i * Fate.COUNT + 3)\n : Atomics.store(this, i * Fate.COUNT + 3, d1)\n }\n\n data1(i: number, d2?: number) {\n return d2 === undefined\n ? Atomics.load(this, i * Fate.COUNT + 4)\n : Atomics.store(this, i * Fate.COUNT + 4, d2)\n }\n\n data2(i: number, d3?: number) {\n return d3 === undefined\n ? Atomics.load(this, i * Fate.COUNT + 5)\n : Atomics.store(this, i * Fate.COUNT + 5, d3)\n }\n}\n\nfunction splice(str, index, count, add = '') {\n return `${str.slice(0, index)}${add}${str.slice(index + count)}`\n}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT, IMPACTS_MAX_PER } from 'src/config'\nimport { EImpactReaction } from 'src/fate/weave'\n\nexport class Impact extends AtomicInt {\n // [who]\n static COUNT = IMPACTS_MAX_PER + 1\n\n constructor(shared = new SharedArrayBuffer(ATOM_COUNT * Impact.COUNT * 4)) {\n super(shared)\n }\n\n reaction(id: number, resolver?: EImpactReaction) {\n return resolver === undefined\n ? Atomics.load(this, id * Impact.COUNT)\n : Atomics.store(this, id * Impact.COUNT, resolver)\n }\n\n impact(id: number, offset: number, who?: number) {\n return who === undefined\n ? Atomics.load(this, id * Impact.COUNT + offset)\n : Atomics.store(this, id * Impact.COUNT + offset, who)\n }\n}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\n\nexport class Matter extends AtomicInt {\n static COUNT = 3\n\n constructor(shared = new SharedArrayBuffer(ATOM_COUNT * Matter.COUNT * 4)) {\n super(shared)\n }\n\n red(i: number, r?: number) {\n return r !== undefined\n ? Atomics.store(this, i * Matter.COUNT, r)\n : Atomics.load(this, i * Matter.COUNT)\n }\n\n green(i: number, g?: number) {\n return g !== undefined\n ? Atomics.store(this, i * Matter.COUNT + 1, g)\n : Atomics.load(this, i * Matter.COUNT + 1)\n }\n\n blue(i: number, b?: number) {\n return b !== undefined\n ? Atomics.store(this, i * Matter.COUNT + 2, b)\n : Atomics.load(this, i * Matter.COUNT + 2)\n }\n}\n","import { ATOM_COUNT } from 'src/config'\nimport { Box3, Vector3 } from 'three'\nimport { AtomicInt } from './atomic'\nimport { Vec3 } from './vec3'\n\nexport class BBox extends Box3 {\n i: number\n constructor(i: number = 0) {\n super()\n this.i = i\n }\n\n get minX() {\n return this.min.x\n }\n get minY() {\n return this.min.y\n }\n get minZ() {\n return this.min.z\n }\n get maxX() {\n return this.max.x\n }\n get maxY() {\n return this.max.y\n }\n get maxZ() {\n return this.max.z\n }\n}\n\nconst $box = new BBox(0)\nconst $vec3 = new Vector3()\n\nexport class Size extends AtomicInt {\n static COUNT = 4\n\n box(i: number, future: Vec3, $bb: BBox = $box) {\n const sx = this.x(i) / 2,\n sy = this.y(i) / 2,\n sz = this.z(i) / 2,\n x = future.x(i),\n y = future.y(i),\n z = future.z(i)\n\n $bb.min.set(x - sx, y - sy, z - sz), $bb.max.set(x + sx, y + sy, z + sz)\n\n $bb.i = i\n return $bb\n }\n\n constructor(buffer = new SharedArrayBuffer(ATOM_COUNT * Size.COUNT * 4)) {\n super(buffer)\n }\n\n addX(i: number, x: number) {\n return Atomics.add(this, i * Size.COUNT, x)\n }\n\n addY(i: number, y: number) {\n return Atomics.add(this, i * Size.COUNT + 1, y)\n }\n\n addZ(i: number, z: number) {\n return Atomics.add(this, i * Size.COUNT + 2, z)\n }\n\n x(i: number, x?: number) {\n return x === undefined\n ? Atomics.load(this, i * Size.COUNT)\n : Atomics.store(this, i * Size.COUNT, x)\n }\n\n y(i: number, y?: number) {\n return y === undefined\n ? Atomics.load(this, i * Size.COUNT + 1)\n : Atomics.store(this, i * Size.COUNT + 1, y)\n }\n\n z(i: number, z?: number) {\n return z === undefined\n ? Atomics.load(this, i * Size.COUNT + 2)\n : Atomics.store(this, i * Size.COUNT + 2, z)\n }\n vec3(i: number, vec3 = $vec3) {\n return vec3.set(this.x(i), this.y(i), this.z(i))\n }\n\n setVec3(i: number, vec3: Vector3) {\n this.x(i, Math.round(vec3.x))\n this.y(i, Math.round(vec3.y))\n this.z(i, Math.round(vec3.z))\n return vec3\n }\n\n pattern(i: number, glyph?: number) {\n return glyph === undefined\n ? Atomics.load(this, i * Size.COUNT + 3)\n : Atomics.store(this, i * Size.COUNT + 3, glyph)\n }\n}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\nimport { Vector3 } from 'three'\n\nconst $vec3 = new Vector3()\n\nexport class SpaceTime extends AtomicInt {\n static COUNT = 4\n\n constructor(\n shared = new SharedArrayBuffer(ATOM_COUNT * 4 * SpaceTime.COUNT)\n ) {\n super(shared)\n }\n addX(i: number, x: number) {\n return Atomics.add(this, i * SpaceTime.COUNT, x)\n }\n addY(i: number, y: number) {\n return Atomics.add(this, i * SpaceTime.COUNT + 1, y)\n }\n\n addZ(i: number, z: number) {\n return Atomics.add(this, i * SpaceTime.COUNT + 2, z)\n }\n\n x(i: number, x?: number) {\n return x === undefined\n ? Atomics.load(this, i * SpaceTime.COUNT)\n : Atomics.store(this, i * SpaceTime.COUNT, x)\n }\n y(i: number, y?: number) {\n return y === undefined\n ? Atomics.load(this, i * SpaceTime.COUNT + 1)\n : Atomics.store(this, i * SpaceTime.COUNT + 1, y)\n }\n z(i: number, z?: number) {\n return z === undefined\n ? Atomics.load(this, i * SpaceTime.COUNT + 2)\n : Atomics.store(this, i * SpaceTime.COUNT + 2, z)\n }\n time(i: number, t?: number) {\n return t === undefined\n ? Atomics.load(this, i * SpaceTime.COUNT + 3)\n : Atomics.store(this, i * SpaceTime.COUNT + 3, t)\n }\n\n vec3(i: number, vec3 = $vec3) {\n return vec3.set(this.x(i), this.y(i), this.z(i))\n }\n setVec3(i: number, vec3: Vector3) {\n this.x(i, Math.round(vec3.x))\n this.y(i, Math.round(vec3.y))\n this.z(i, Math.round(vec3.z))\n return vec3\n }\n}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { ATOM_COUNT } from 'src/config'\nimport { Vector3 } from 'three'\n\nconst $vec3 = new Vector3()\n\nexport class Vec3 extends AtomicInt {\n static COUNT = 3\n\n constructor(buffer = new SharedArrayBuffer(ATOM_COUNT * Vec3.COUNT * 4)) {\n super(buffer)\n }\n addX(i: number, x: number) {\n return Atomics.add(this, i * Vec3.COUNT, x)\n }\n addY(i: number, y: number) {\n return Atomics.add(this, i * Vec3.COUNT + 1, y)\n }\n\n addZ(i: number, z: number) {\n return Atomics.add(this, i * Vec3.COUNT + 2, z)\n }\n x(i: number, x?: number) {\n return x === undefined\n ? Atomics.load(this, i * Vec3.COUNT)\n : Atomics.store(this, i * Vec3.COUNT, x)\n }\n\n y(i: number, y?: number) {\n return y === undefined\n ? Atomics.load(this, i * Vec3.COUNT + 1)\n : Atomics.store(this, i * Vec3.COUNT + 1, y)\n }\n\n z(i: number, z?: number) {\n return z === undefined\n ? Atomics.load(this, i * Vec3.COUNT + 2)\n : Atomics.store(this, i * Vec3.COUNT + 2, z)\n }\n vec3(i: number, vec3 = $vec3) {\n return vec3.set(this.x(i), this.y(i), this.z(i))\n }\n setVec3(i: number, vec3: Vector3) {\n this.x(i, Math.round(vec3.x))\n this.y(i, Math.round(vec3.y))\n this.z(i, Math.round(vec3.z))\n return vec3\n }\n}\n","import { Vec3 } from './vec3'\n\nexport class Thrust extends Vec3 {}\n","import { AtomicInt } from 'src/buffer/atomic'\nimport { UNIVERSALS } from 'src/config'\nimport { EIdle } from 'src/fate/weave'\nimport { Box3, Vector3 } from 'three'\n\nconst $cage = new Box3()\nconst $offset = new Vector3()\nconst $vec = new Vector3()\n\nexport enum ERealmState {\n PAUSED = 0,\n RUNNING,\n}\n\nexport class Universal extends AtomicInt {\n // 3 * 10 for the hand vectors\n static COUNT = 39\n _init = false\n\n constructor(shared = new SharedArrayBuffer(4 * Universal.COUNT)) {\n super(shared)\n this.reset()\n }\n\n reset() {\n for (let i = 0; i < UNIVERSALS.length; i++) {\n this.store(i, UNIVERSALS[i])\n }\n this._init = true\n return this\n }\n\n time(t?: number) {\n return t === undefined ? Atomics.load(this, 0) : Atomics.store(this, 0, t)\n }\n\n faeSize(size?: number) {\n return size === undefined\n ? Atomics.load(this, 1)\n : Atomics.store(this, 1, size)\n }\n\n idle(idle?: EIdle) {\n return idle === undefined\n ? Atomics.load(this, 2)\n : Atomics.store(this, 2, idle)\n }\n\n clearColor(color?: number) {\n return color === undefined\n ? Atomics.load(this, 3)\n : Atomics.store(this, 3, color)\n }\n\n faeX(x?: number) {\n return x === undefined ? Atomics.load(this, 4) : Atomics.store(this, 4, x)\n }\n\n faeY(y?: number) {\n return y === undefined ? Atomics.load(this, 5) : Atomics.store(this, 5, y)\n }\n\n faeZ(z?: number) {\n return z === undefined ? Atomics.load(this, 6) : Atomics.store(this, 6, z)\n }\n\n musicTime(t?: number) {\n return t === undefined ? Atomics.load(this, 7) : Atomics.store(this, 7, t)\n }\n\n faeRX(rx?: number) {\n return rx === undefined ? Atomics.load(this, 8) : Atomics.store(this, 8, rx)\n }\n\n faeRY(ry?: number) {\n return ry === undefined ? Atomics.load(this, 9) : Atomics.store(this, 9, ry)\n }\n faeRZ(rz?: number) {\n return rz === undefined\n ? Atomics.load(this, 10)\n : Atomics.store(this, 10, rz)\n }\n\n cageX(x?: number) {\n return x === undefined ? Atomics.load(this, 11) : Atomics.store(this, 11, x)\n }\n cageY(y?: number) {\n return y === undefined ? Atomics.load(this, 12) : Atomics.store(this, 12, y)\n }\n cageZ(z?: number) {\n return z === undefined ? Atomics.load(this, 13) : Atomics.store(this, 13, z)\n }\n cageMX(mx?: number) {\n return mx === undefined\n ? Atomics.load(this, 14)\n : Atomics.store(this, 14, mx)\n }\n cageMY(my?: number) {\n return my === undefined\n ? Atomics.load(this, 15)\n : Atomics.store(this, 15, my)\n }\n cageMZ(mz?: number) {\n return mz === undefined\n ? Atomics.load(this, 16)\n : Atomics.store(this, 16, mz)\n }\n\n offsetX(x?: number) {\n return x === undefined ? Atomics.load(this, 17) : Atomics.store(this, 17, x)\n }\n offsetY(y?: number) {\n return y === undefined ? Atomics.load(this, 18) : Atomics.store(this, 18, y)\n }\n offsetZ(z?: number) {\n return z === undefined ? Atomics.load(this, 19) : Atomics.store(this, 19, z)\n }\n\n offset(offset?: Vector3) {\n if (offset === undefined) {\n $offset.set(this.offsetX(), this.offsetY(), this.offsetZ())\n return $offset\n } else {\n this.offsetX(offset.x)\n this.offsetY(offset.y)\n this.offsetZ(offset.z)\n return offset\n }\n }\n\n cage(cage?: Box3) {\n if (cage === undefined) {\n $cage.min.set(this.cageX(), this.cageY(), this.cageZ())\n $cage.max.set(this.cageMX(), this.cageMY(), this.cageMZ())\n return $cage\n } else {\n this.cageX(cage.min.x)\n this.cageY(cage.min.y)\n this.cageZ(cage.min.z)\n this.cageMX(cage.max.x)\n this.cageMY(cage.max.y)\n this.cageMZ(cage.max.z)\n return cage\n }\n }\n\n state(state?: ERealmState) {\n return state === undefined\n ? Atomics.load(this, 20)\n : Atomics.store(this, 20, state)\n }\n\n avatar(avatar?: number) {\n return avatar === undefined\n ? Atomics.load(this, 21)\n : Atomics.store(this, 21, avatar)\n }\n\n thrustStrength(strength?: number) {\n return strength === undefined\n ? Atomics.load(this, 22)\n : Atomics.store(this, 22, strength)\n }\n\n score(score?: number) {\n return score === undefined\n ? Atomics.load(this, 23)\n : Atomics.store(this, 23, score)\n }\n\n gravityX(gravity?: number) {\n return gravity === undefined\n ? Atomics.load(this, 24)\n : Atomics.store(this, 24, gravity)\n }\n\n gravityY(gravity?: number) {\n return gravity === undefined\n ? Atomics.load(this, 25)\n : Atomics.store(this, 25, gravity)\n }\n gravityZ(gravity?: number) {\n return gravity === undefined\n ? Atomics.load(this, 26)\n : Atomics.store(this, 26, gravity)\n }\n faeHue(color?: number) {\n return color === undefined\n ? Atomics.load(this, 27)\n : Atomics.store(this, 27, color)\n }\n faeHueVariance(variance?: number) {\n return variance === undefined\n ? Atomics.load(this, 28)\n : Atomics.store(this, 28, variance)\n }\n\n faeHand(hand: number, number?: number) {\n if (number === undefined) {\n return Atomics.load(this, 29 + hand)\n } else {\n Atomics.store(this, 29 + hand, number)\n return number\n }\n }\n}\n","import { Vec3 } from './vec3'\n\nexport class Velocity extends Vec3 {}\n","// parse magica voxel files\nexport const colors_default = [\n 0x000000, 0xffffff, 0xccffff, 0x99ffff, 0x66ffff, 0x33ffff, 0x00ffff,\n 0xffccff, 0xccccff, 0x99ccff, 0x66ccff, 0x33ccff, 0x00ccff, 0xff99ff,\n 0xcc99ff, 0x9999ff, 0x6699ff, 0x3399ff, 0x0099ff, 0xff66ff, 0xcc66ff,\n 0x9966ff, 0x6666ff, 0x3366ff, 0x0066ff, 0xff33ff, 0xcc33ff, 0x9933ff,\n 0x6633ff, 0x3333ff, 0x0033ff, 0xff00ff, 0xcc00ff, 0x9900ff, 0x6600ff,\n 0x3300ff, 0x0000ff, 0xffffcc, 0xccffcc, 0x99ffcc, 0x66ffcc, 0x33ffcc,\n 0x00ffcc, 0xffcccc, 0xcccccc, 0x99cccc, 0x66cccc, 0x33cccc, 0x00cccc,\n 0xff99cc, 0xcc99cc, 0x9999cc, 0x6699cc, 0x3399cc, 0x0099cc, 0xff66cc,\n 0xcc66cc, 0x9966cc, 0x6666cc, 0x3366cc, 0x0066cc, 0xff33cc, 0xcc33cc,\n 0x9933cc, 0x6633cc, 0x3333cc, 0x0033cc, 0xff00cc, 0xcc00cc, 0x9900cc,\n 0x6600cc, 0x3300cc, 0x0000cc, 0xffff99, 0xccff99, 0x99ff99, 0x66ff99,\n 0x33ff99, 0x00ff99, 0xffcc99, 0xcccc99, 0x99cc99, 0x66cc99, 0x33cc99,\n 0x00cc99, 0xff9999, 0xcc9999, 0x999999, 0x669999, 0x339999, 0x009999,\n 0xff6699, 0xcc6699, 0x996699, 0x666699, 0x336699, 0x006699, 0xff3399,\n 0xcc3399, 0x993399, 0x663399, 0x333399, 0x003399, 0xff0099, 0xcc0099,\n 0x990099, 0x660099, 0x330099, 0x000099, 0xffff66, 0xccff66, 0x99ff66,\n 0x66ff66, 0x33ff66, 0x00ff66, 0xffcc66, 0xcccc66, 0x99cc66, 0x66cc66,\n 0x33cc66, 0x00cc66, 0xff9966, 0xcc9966, 0x999966, 0x669966, 0x339966,\n 0x009966, 0xff6666, 0xcc6666, 0x996666, 0x666666, 0x336666, 0x006666,\n 0xff3366, 0xcc3366, 0x993366, 0x663366, 0x333366, 0x003366, 0xff0066,\n 0xcc0066, 0x990066, 0x660066, 0x330066, 0x000066, 0xffff33, 0xccff33,\n 0x99ff33, 0x66ff33, 0x33ff33, 0x00ff33, 0xffcc33, 0xcccc33, 0x99cc33,\n 0x66cc33, 0x33cc33, 0x00cc33, 0xff9933, 0xcc9933, 0x999933, 0x669933,\n 0x339933, 0x009933, 0xff6633, 0xcc6633, 0x996633, 0x666633, 0x336633,\n 0x006633, 0xff3333, 0xcc3333, 0x993333, 0x663333, 0x333333, 0x003333,\n 0xff0033, 0xcc0033, 0x990033, 0x660033, 0x330033, 0x000033, 0xffff00,\n 0xccff00, 0x99ff00, 0x66ff00, 0x33ff00, 0x00ff00, 0xffcc00, 0xcccc00,\n 0x99cc00, 0x66cc00, 0x33cc00, 0x00cc00, 0xff9900, 0xcc9900, 0x999900,\n 0x669900, 0x339900, 0x009900, 0xff6600, 0xcc6600, 0x996600, 0x666600,\n 0x336600, 0x006600, 0xff3300, 0xcc3300, 0x993300, 0x663300, 0x333300,\n 0x003300, 0xff0000, 0xcc0000, 0x990000, 0x660000, 0x330000, 0x0000ee,\n 0x0000dd, 0x0000bb, 0x0000aa, 0x000088, 0x000077, 0x000055, 0x000044,\n 0x000022, 0x000011, 0x00ee00, 0x00dd00, 0x00bb00, 0x00aa00, 0x008800,\n 0x007700, 0x005500, 0x004400, 0x002200, 0x001100, 0xee0000, 0xdd0000,\n 0xbb0000, 0xaa0000, 0x880000, 0x770000, 0x550000, 0x440000, 0x220000,\n 0x110000, 0xeeeeee, 0xdddddd, 0xbbbbbb, 0xaaaaaa, 0x888888, 0x777777,\n 0x555555, 0x444444, 0x222222, 0x111111,\n].reduce((arr, val, i) => {\n const idx = i * 4\n arr[idx] = (val >> 16) & 0xff\n arr[idx + 1] = (val >> 8) & 0xff\n arr[idx + 2] = val & 0xff\n arr[idx + 3] = 0xff\n return arr\n}, new Uint8Array(256 * 4))\n\nconst INT_SIZE = 4\n\nexport class MagickaVoxel {\n view: DataView\n xyzi: Uint8Array\n rgba: Uint8Array\n\n constructor(data: ArrayBufferLike | DataView) {\n this.view = data instanceof DataView ? data : new DataView(data)\n\n this.chunk()\n if (!this.rgba) {\n this.rgba = colors_default\n }\n }\n\n version() {\n return this.view.getInt32(4, true)\n }\n\n data(id: string, start: number) {\n let cursor = start\n\n switch (id) {\n case 'RGBA':\n this.rgba = new Uint8Array(this.view.buffer, cursor, 256 * 4)\n\n break\n case 'XYZI':\n const count = this.view.getInt32(cursor, true)\n cursor += INT_SIZE\n\n this.xyzi = new Uint8Array(this.view.buffer, cursor, count * 4)\n\n break\n }\n }\n\n length() {\n return this.xyzi.length / 4\n }\n\n chunk(start: number = 8) {\n let cursor = start\n const id = this.readID(cursor)\n\n cursor += INT_SIZE\n\n const chunkBytes = this.view.getInt32(cursor, true)\n cursor += INT_SIZE\n\n const childBytes = this.view.getInt32(cursor, true)\n cursor += INT_SIZE\n\n const end = cursor + chunkBytes + childBytes\n\n // nothing here\n if (chunkBytes + childBytes === 0) return\n\n if (chunkBytes > 0 && id) {\n this.data(id, cursor)\n }\n\n if (childBytes > 0) {\n return this.chunk(cursor + chunkBytes)\n }\n\n // siblings\n if (end !== this.view.byteLength) {\n this.chunk(end)\n }\n }\n\n readID(idx: number): string {\n let id = ''\n for (let i = idx; i < idx + 4; i++) {\n id += String.fromCharCode(this.view.getUint8(i))\n }\n return id\n }\n}\n","import { Fate } from 'src/buffer/fate'\nimport { MagickaVoxel } from 'src/magica'\nimport { Realm } from 'src/realm'\nimport { Value } from 'src/value'\n\n// Load .fate file into the timePline\nexport const SIGNATURE = 'THEA'\nexport const HEADER_START = 4 * 4\n\nexport const HEADER_END = HEADER_START + 4 * 4\n\n// map json ID to timeline ID\n\nexport function Load(bytes: ArrayBuffer, realm: Realm) {\n const { fate: timeline } = realm\n\n try {\n const view = new DataView(bytes)\n\n // check for THEA file type\n for (let i = 0; i < SIGNATURE.length; i++) {\n if (view.getUint8(i) !== SIGNATURE.charCodeAt(i)) {\n throw new Error('Not a valid THEIA file')\n }\n }\n\n // Timeline\n const timeLength = view.getInt32(HEADER_START)\n const timeEnd = HEADER_END + timeLength\n\n timeline.$.freeAll()\n timeline.poke()\n for (let i = 0; i < timeLength / 4; i++) {\n const val = view.getInt32(HEADER_END + i * 4)\n timeline.$.store(i, val)\n\n // remove indexes from available as you come across them\n if (i % Fate.COUNT === 1 && val !== 0) {\n timeline.$.available.splice(\n timeline.$.available.indexOf(Math.floor(i / Fate.COUNT)),\n 1\n )\n }\n }\n\n // Music\n const musicLength = view.getInt32(HEADER_START + 4)\n const musicEnd = HEADER_END + musicLength + timeLength\n\n // skip music if they're not the current reality\n if (musicLength > 0) {\n // 16 for string name\n const mab = new ArrayBuffer(musicLength - 12)\n const music = new DataView(mab)\n\n let str = ''\n for (let i = 0; i < 12; i++) {\n const v = view.getUint8(timeEnd + i)\n if (v === 0) break\n str += String.fromCharCode(v)\n }\n\n for (let i = 0; i < (musicLength - 12) / 4 - 1; i++) {\n music.setInt32(i * 4, view.getInt32(timeEnd + i * 4 + 12))\n }\n\n realm.musicBuffer = new DataView(mab)\n realm.musicName = str\n realm.musicString = URL.createObjectURL(new File([mab], 'thea'))\n }\n\n // clear existing vox\n\n // Vox\n const voxLength = view.getInt32(HEADER_START + 4 + 4)\n const voxUpdate = {}\n if (voxLength > 0) {\n //size[ 12 char string name, vox raw data]size[]\n // rip through and read these\n\n let cursor = 0\n let size = 0\n\n while (cursor < voxLength) {\n if (size === 0) {\n size = view.getInt32(musicEnd + cursor)\n cursor += 4\n }\n let str = ''\n // decode string here\n for (let i = 0; i < 12; i++) {\n const c = view.getUint8(musicEnd + cursor + i)\n if (c === 0) continue\n\n str += String.fromCharCode(c)\n }\n cursor += 12\n\n // Slice so you can share the data to the worker\n voxUpdate[str] = new MagickaVoxel(\n new DataView(bytes.slice(cursor + musicEnd, cursor + musicEnd + size))\n )\n\n cursor += size\n size = 0\n }\n }\n realm.voxes.set(voxUpdate)\n // only poke at the end incase we need to revert\n timeline.poke()\n } catch (ex) {\n // undo that garbo\n timeline.$.freeAll()\n timeline.poke()\n throw ex\n }\n}\n\nexport const dbLoaded = new Value(false)\n","import { Value } from 'src/value'\nimport { Uniform, Vector3 } from 'three'\nimport { IJointGroup } from './joints'\nimport { left, Phony, right } from './phony'\n\nexport { left_controller, right_controller } from './phony'\n\nexport const last_pose = new Value({\n left: '',\n right: '',\n})\n\nexport const pose = new Value({\n left: '',\n right: '',\n})\n\nexport const hands = new Value([\n new Phony(left),\n new Phony(right, 'right'),\n])\n\nexport const left_hand = new Value(hands.$[0])\nexport const right_hand = new Value(hands.$[1])\nexport const VRInit = new Value(false)\n\nexport const left_hand_uniforms = {\n ['thumb-tip']: new Uniform(new Vector3()),\n ['index-finger-tip']: new Uniform(new Vector3()),\n ['middle-finger-tip']: new Uniform(new Vector3()),\n ['ring-finger-tip']: new Uniform(new Vector3()),\n ['pinky-finger-tip']: new Uniform(new Vector3()),\n}\n\nexport const right_hand_uniforms = {\n ['thumb-tip']: new Uniform(new Vector3()),\n ['index-finger-tip']: new Uniform(new Vector3()),\n ['middle-finger-tip']: new Uniform(new Vector3()),\n ['ring-finger-tip']: new Uniform(new Vector3()),\n ['pinky-finger-tip']: new Uniform(new Vector3()),\n}\n","// Positives are used to refer to entity IDs\n// Negatives for messages\n\nimport { Animation } from 'src/buffer/animation'\nimport { Cage } from 'src/buffer/cage'\nimport { Fate } from 'src/buffer/fate'\nimport { Impact } from 'src/buffer/impact'\nimport { Matter } from 'src/buffer/matter'\nimport { Phys } from 'src/buffer/phys'\nimport { Size } from 'src/buffer/size'\nimport { SpaceTime } from 'src/buffer/spacetime'\nimport { Thrust } from 'src/buffer/thrust'\nimport { Traits } from 'src/buffer/traits'\nimport { Universal } from 'src/buffer/universal'\nimport { Velocity } from 'src/buffer/velocity'\nimport { ESelectThen } from 'src/fate/weave'\nimport { MagickaVoxel } from 'src/magica'\nimport { Value } from 'src/value'\n\nexport enum EMessage {\n REZ = -1,\n FREE_ALL = -404,\n UNI_SCORE = -1000,\n FATE_UPDATE = -2000,\n CLEAR_COLOR_UPDATE = -2002,\n FAE_POS_UPDATE = -2003,\n FAE_ROT_UPDATE = -2004,\n FAE_NOTIFY = -2005,\n PHYS_COLLIDE = -3000,\n SENSE_TICK = -4000,\n LAND_ADD = -5001,\n LAND_REMOVE = -5002,\n PHYS_TICK = -6000,\n PHYS_SELECT = -6001,\n CARD_TICK = -7000,\n CARD_AVATAR = -7001,\n CARD_MIDI = -7002,\n CARD_SEEK = -7003,\n CARD_SEEKED = -7004,\n CARD_MIDI_CHIRP = -7005,\n // Load the specified file\n CARD_LOAD = -7006,\n CARD_GATE = -7007,\n YGG_REALM_UPDATE = -8000,\n YGG_HOST = -8001,\n YGG_JOIN = -8002,\n}\n\nexport enum ENotifyPosition {\n BETWEEN_HANDS = 0,\n LEFT_HAND,\n RIGHT_HAND,\n}\n\nexport type FRez = () => number\n\nexport interface ICardinal {\n // entity components\n past: SpaceTime\n future: SpaceTime\n matter: Matter\n thrust: Thrust\n size: Size\n impact: Impact\n animation: Animation\n traits: Traits\n velocity: Velocity\n phys: Phys\n\n fate: Fate\n universal: Universal\n cage: Cage\n\n ready: boolean\n\n lastTime: number\n clutchFate: boolean\n\n voxes: Value<{ [name: string]: MagickaVoxel }>\n\n free(i: number)\n post(message: any)\n reserve(): number\n}\n\nexport interface IPhysSelect {\n min: { x: number; y: number; z: number }\n max: { x: number; y: number; z: number }\n message: EMessage.PHYS_SELECT\n do: ESelectThen\n is: string\n not: string\n}\n","import { IAtomic } from 'src/buffer/atomic'\nimport { ICancel, Value } from 'src/value'\n\nexport type IMessage = IAtomic | number | object | string\n\nexport class SystemWorker extends Worker {\n _delay = 0\n _queue = []\n msg = new Value()\n\n cancels: ICancel[] = []\n\n constructor(url: string) {\n super(url)\n\n this.onmessage = this.message.bind(this)\n }\n\n terminate() {\n super.terminate()\n this.cancels.forEach((c) => c())\n }\n\n // delay in ms before sending buffers\n delay(ms: number) {\n this._delay = ms\n }\n\n send(...buffers: IMessage[]) {\n for (let b of buffers) {\n // @ts-ignore - send the SharedArrayBuffer for our atomic types\n this.postMessage(b.sab !== undefined ? b.sab : b)\n }\n\n return this\n }\n\n waitForEntity(e: (msg: any) => void) {\n this._queue.push(e)\n }\n\n message(e: MessageEvent) {\n // >= 0 are eids while - are commands\n if (typeof e.data === 'number' && e.data >= 0 && this._queue.length) {\n const i = this._queue.pop()\n\n if (i) {\n i(e.data)\n }\n }\n\n this.msg.set(e.data)\n }\n\n // pipe received messages to other worker\n pump(w: SystemWorker) {\n this.cancels.push(\n this.msg.on((data) => {\n switch (true) {\n case data instanceof ArrayBuffer:\n break\n default:\n w.postMessage(data)\n break\n }\n })\n )\n return this\n }\n\n // only relay numerical messages\n bind(w: SystemWorker) {\n this.cancels.push(\n w.msg.on((data) => {\n switch (true) {\n case data instanceof ArrayBuffer:\n break\n default:\n this.postMessage(data)\n break\n }\n })\n )\n return this\n }\n\n onCancel(e: (data: any) => void) {\n const f = this.msg.on(e)\n this.cancels.push(f)\n\n return () => {\n this.cancels.splice(this.cancels.indexOf(f), 1)\n f()\n }\n }\n on(e: (data: any) => void) {\n this.cancels.push(this.msg.on(e))\n\n return this\n }\n}\n\n// How to spin up and communicate with systems\nexport class Sys {\n $: Set\n\n constructor() {\n this.$ = new Set()\n }\n\n start(worker: string): SystemWorker {\n const w = new SystemWorker(`/build/${worker}.js`)\n this.$.add(w)\n return w\n }\n\n destroy(worker: SystemWorker) {\n worker.terminate()\n this.$.delete(worker)\n }\n}\n\nexport const sys = new Sys()\n","import { AtomicByte } from 'src/buffer/atomic'\n\n// TODO: Not sure if wanted, but seems likely\nexport class Input extends AtomicByte {\n // 3 * 10 for the hand vectors\n static COUNT = 8 * 2\n\n constructor(shared = new SharedArrayBuffer(Input.COUNT)) {\n super(shared)\n }\n pinching(p?: boolean) {\n if (p === undefined) return Atomics.load(this, 0) === 1\n\n return Atomics.store(this, 0, p ? 1 : 0)\n }\n grabbing(g?: boolean) {\n if (g === undefined) return Atomics.load(this, 1) === 1\n\n return Atomics.store(this, 1, g ? 1 : 0)\n }\n // defined by the weave\n weave(i: number, w?: boolean) {\n if (w === undefined) return Atomics.load(this, 2 + i) === 1\n\n return Atomics.store(this, 2 + i, w ? 1 : 0)\n }\n\n idle() {\n return !(\n this.pinching() &&\n this.grabbing() &&\n this.weave(0) &&\n this.weave(1) &&\n this.weave(2) &&\n this.weave(3) &&\n this.weave(4) &&\n this.weave(5)\n )\n }\n\n pinchingRight(p?: boolean) {\n if (p === undefined) return Atomics.load(this, 8) === 1\n\n return Atomics.store(this, 8, p ? 1 : 0)\n }\n grabbingRight(g?: boolean) {\n if (g === undefined) return Atomics.load(this, 1) === 1\n\n return Atomics.store(this, 9, g ? 1 : 0)\n }\n // defined by the weave\n weaveRight(i: number, w?: boolean) {\n if (w === undefined) return Atomics.load(this, 2 + i) === 1\n\n return Atomics.store(this, 10 + i, w ? 1 : 0)\n }\n\n idleRight() {\n return !(\n this.pinchingRight() &&\n this.grabbingRight() &&\n this.weaveRight(0) &&\n this.weaveRight(1) &&\n this.weaveRight(2) &&\n this.weaveRight(3) &&\n this.weaveRight(4) &&\n this.weaveRight(5)\n )\n }\n}\n","import { ATOM_COUNT } from 'src/config'\nimport { AtomicInt } from './atomic'\n\nexport enum ESenses {\n NONE = 0,\n SIGHT = 1,\n HEAR = 2,\n FEEL_RIGHT = 4,\n FEEL_LEFT = 8,\n FELT = 16,\n TASTE = 32,\n}\n\n// distance is on phys since its calced for every atom anyhow\nexport class Sensed extends AtomicInt {\n static COUNT = 3\n\n constructor(buffer = new SharedArrayBuffer(ATOM_COUNT * Sensed.COUNT * 4)) {\n super(buffer)\n }\n\n id(i: number, id?: number): number {\n return id === undefined\n ? Atomics.load(this, Sensed.COUNT * i)\n : Atomics.store(this, Sensed.COUNT * i, id)\n }\n\n sense(i: number, sense?: number): number {\n return sense === undefined\n ? Atomics.load(this, Sensed.COUNT * i + 1)\n : Atomics.store(this, Sensed.COUNT * i + 1, sense)\n }\n\n pan(i: number, pan?: number): number {\n return pan === undefined\n ? Atomics.load(this, Sensed.COUNT * i + 2)\n : Atomics.store(this, Sensed.COUNT * i + 2, pan)\n }\n}\n","import { Input } from 'src/buffer/input'\nimport { Matter } from 'src/buffer/matter'\nimport { Size } from 'src/buffer/size'\nimport { SpaceTime } from 'src/buffer/spacetime'\nimport { YGGDRASIL } from 'src/config'\nimport { EMessage } from './enum'\nimport { LocalSystem } from './system'\n\nexport class Yggdrasil extends LocalSystem {\n remote: RTCPeerConnection\n data: RTCDataChannel\n\n future: SpaceTime\n matter: Matter\n size: Size\n input: Input\n realm: string\n\n host = false\n\n constructor() {\n super(200)\n }\n\n onmessage(e: { data: any }) {\n switch (undefined) {\n case this.future:\n this.future = new SpaceTime(e.data)\n break\n\n case this.matter:\n this.matter = new Matter(e.data)\n break\n\n case this.size:\n this.size = new Size(e.data)\n break\n\n case this.realm:\n this.realm = e.data\n\n break\n case this.input:\n this.input = new Input(e.data)\n this.init()\n break\n }\n }\n\n connect() {\n if (this.remote) {\n this.remote.close()\n }\n\n this.remote = new RTCPeerConnection({\n iceServers: [\n {\n urls: [\n 'stun:stun3.l.google.com:19302',\n 'stun:stun2.l.google.com:19302',\n ],\n },\n ],\n })\n\n this.remote.onnegotiationneeded = this.OnNegotiate.bind(this)\n\n this.data = this.remote.createDataChannel(this.realm)\n this.data.onmessage = this.dataMessage.bind(this)\n }\n\n dataMessage(e: { data: any }) {\n const command = parseInt(e.data, 10)\n if (isNaN(command)) return\n\n switch (command) {\n case EMessage.YGG_HOST:\n this.host = true\n break\n case EMessage.YGG_JOIN:\n // we joined the realm\n break\n }\n }\n\n OnNegotiate(e) {\n this.remote\n .createOffer()\n .then((d) => {\n this.remote.setLocalDescription(d)\n\n fetch(YGGDRASIL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Channel: this.realm,\n },\n body: JSON.stringify(d),\n mode: 'cors',\n })\n .then((r) => r.json())\n .then((answer) => {\n const description = new RTCSessionDescription(answer)\n this.remote.setRemoteDescription(description)\n })\n .catch(() => {\n setTimeout(() => {\n this.OnNegotiate(e)\n }, 10000)\n })\n })\n .catch((err) => {\n console.error(err)\n })\n }\n\n tick() {\n // send the buffers off\n }\n\n init() {\n if (this.realm === '' || this.realm === undefined) return\n\n this.connect()\n }\n}\n","import { SystemWorker } from './sys'\n\n// systems run as their own workers\nexport class System {\n tickrate: number\n constructor(tickrate: number = (1 / 5) * 1000) {\n // start up worker\n self.onmessage = this.onmessage.bind(this)\n\n this.tickrate = tickrate\n if (tickrate === 0) return\n setInterval(this.tick.bind(this), tickrate)\n }\n\n onmessage(msg: MessageEvent) {}\n\n post(data: any) {\n self.postMessage(data, undefined)\n }\n\n tick() {}\n}\n\nexport class LocalSystem {\n tickrate: number\n constructor(tickrate: number = (1 / 5) * 1000) {\n this.tickrate = tickrate\n if (tickrate === 0) return\n setInterval(this.tick.bind(this), tickrate)\n }\n\n onmessage(msg: { data: any }) {}\n\n post(data: any) {}\n\n tick() {}\n\n send(...stuff: any[]) {\n for (let item of stuff) {\n this.onmessage({ data: item })\n }\n return this\n }\n\n bind(sys: SystemWorker) {\n sys.msg.on((data) => {\n switch (typeof data) {\n case 'number':\n this.onmessage({ data })\n break\n }\n })\n return this\n }\n}\n","import { Animation } from 'src/buffer/animation'\r\nimport { Cage } from 'src/buffer/cage'\r\nimport { Fate } from 'src/buffer/fate'\r\nimport { Impact } from 'src/buffer/impact'\r\nimport { Matter } from 'src/buffer/matter'\r\nimport { Size } from 'src/buffer/size'\r\nimport { SpaceTime } from 'src/buffer/spacetime'\r\nimport { Thrust } from 'src/buffer/thrust'\r\nimport { Traits } from 'src/buffer/traits'\r\nimport { Universal } from 'src/buffer/universal'\r\nimport { Velocity } from 'src/buffer/velocity'\r\nimport { ATOM_COUNT, FACES, INFRINGEMENT, NORMALIZER, SIZE } from 'src/config'\r\nimport {\r\n audio,\r\n Chirp,\r\n lowerUniform,\r\n MIDI,\r\n seconds,\r\n upperUniform,\r\n} from 'src/controller/audio'\r\nimport { isQuest, multiplayer, options } from 'src/input/browser'\r\nimport { Load } from 'src/input/load'\r\nimport { left_hand_uniforms, right_hand_uniforms } from 'src/input/xr'\r\nimport { MagickaVoxel } from 'src/magica'\r\nimport { body, camera, renderer, scene } from 'src/render'\r\nimport AnimationFrag from 'src/shader/animation.frag'\r\nimport AnimationVert from 'src/shader/animation.vert'\r\nimport EnumVert from 'src/shader/enum.vert'\r\nimport HeaderVert from 'src/shader/header.vert'\r\nimport MainVert from 'src/shader/main.vert'\r\nimport MatterFrag from 'src/shader/matter.frag'\r\nimport SpaceTimeVert from 'src/shader/spacetime.vert'\r\nimport { timeUniform, timing } from 'src/shader/time'\r\nimport { EMessage } from 'src/system/enum'\r\nimport { sys, SystemWorker } from 'src/system/sys'\r\nimport { ICancel, Value } from 'src/value'\r\nimport {\r\n Box3,\r\n BoxBufferGeometry,\r\n FrontSide,\r\n InstancedBufferAttribute,\r\n InstancedMesh,\r\n Material,\r\n Matrix4,\r\n Mesh,\r\n MeshBasicMaterial,\r\n MeshToonMaterial,\r\n NearestFilter,\r\n RepeatWrapping,\r\n TextureLoader,\r\n Uniform,\r\n Vector3,\r\n} from 'three'\r\nimport { Input } from './buffer/input'\r\nimport { Phys } from './buffer/phys'\r\nimport { Sensed } from './buffer/sensed'\r\nimport { LocalSystem } from './system/system'\r\nimport { Yggdrasil } from './system/yggdrasil'\r\n\r\nconst IDENTITY = new Matrix4().identity()\r\nconst cache = {}\r\n\r\nexport const realms: { [key: number]: Realm } = {}\r\n\r\nlet nextLandCheck = 0\r\n\r\nconst $vec3 = new Vector3()\r\nconst $cage = new Box3()\r\n\r\nlet i = 0\r\n\r\nlet realmId = 0\r\n\r\nObject.assign(window, { realms })\r\n\r\nexport const fantasy = new Value()\r\nexport const first = new Value(undefined)\r\nexport const fantasies = new Value([])\r\nconst tex = new TextureLoader().load('/image/patterns.png')\r\ntex.magFilter = NearestFilter\r\ntex.minFilter = NearestFilter\r\ntex.wrapS = tex.wrapT = RepeatWrapping\r\n\r\ntex.generateMipmaps = true\r\n\r\n// @ts-ignore\r\nwindow.first = first\r\nexport class Realm {\r\n // entity components\r\n past: SpaceTime\r\n future: SpaceTime\r\n matter: Matter\r\n thrust: Thrust\r\n velocity: Velocity\r\n size: Size\r\n impact: Impact\r\n animation: Animation\r\n traits: Traits\r\n phys: Phys\r\n sensed: Sensed\r\n\r\n fate: Value\r\n universal: Universal\r\n input: Input\r\n cage: Cage\r\n\r\n musicName: string\r\n musicBuffer: DataView\r\n musicString: string\r\n\r\n physics: SystemWorker\r\n cardinal: SystemWorker\r\n ai: SystemWorker\r\n senses: SystemWorker\r\n\r\n yggdrasil: LocalSystem\r\n\r\n atoms: InstancedMesh\r\n\r\n fateJSON = new Value({\r\n markers: {},\r\n _: {},\r\n flat: {},\r\n })\r\n\r\n voxes = new Value<{ [name: string]: MagickaVoxel }>({})\r\n material: Material\r\n\r\n uniCage: Uniform\r\n uniCageM: Uniform\r\n uniOffset: Uniform\r\n uniShape: Uniform\r\n\r\n cancels: ICancel[] = []\r\n\r\n destroyed = false\r\n slowFantasy = i++\r\n id = realmId++\r\n\r\n score = new Value(0)\r\n\r\n // the id of the atom that is the fae's avatar\r\n avatar = new Value()\r\n blinder: Mesh\r\n\r\n constructor() {\r\n if (first.$ === undefined) first.set(this)\r\n\r\n fantasies.$.push(this)\r\n this.thrust = new Thrust()\r\n this.velocity = new Velocity()\r\n\r\n this.past = new SpaceTime()\r\n\r\n this.future = new SpaceTime()\r\n this.animation = new Animation()\r\n this.matter = new Matter()\r\n this.impact = new Impact()\r\n this.size = new Size()\r\n this.traits = new Traits()\r\n this.universal = new Universal()\r\n this.cage = new Cage()\r\n this.phys = new Phys()\r\n this.sensed = new Sensed()\r\n this.input = new Input()\r\n\r\n this.fate = new Value(new Fate())\r\n this.initMaterial()\r\n\r\n this.initSystems()\r\n this.initAtoms()\r\n\r\n this.initListeners()\r\n\r\n fantasies.poke()\r\n }\r\n\r\n initMaterial() {\r\n this.material = isQuest ? new MeshBasicMaterial() : new MeshToonMaterial()\r\n\r\n if (!isQuest) {\r\n this.material.shadowSide = FrontSide\r\n }\r\n const commonVertChunk = [\r\n '#include ',\r\n HeaderVert,\r\n EnumVert,\r\n AnimationVert,\r\n SpaceTimeVert,\r\n ].join('\\n')\r\n\r\n const fragmentParsChunk = [\r\n '#include ',\r\n EnumVert,\r\n MatterFrag,\r\n AnimationFrag,\r\n ].join('\\n')\r\n\r\n const colorChunk = [\r\n `vec4 diffuseColor = AnimationFrag(MatterFrag(vec4( diffuse, opacity)));`,\r\n ].join('\\n')\r\n const c = this.universal.cage()\r\n\r\n this.uniCage = new Uniform(c.min.clone())\r\n this.uniCageM = new Uniform(c.max.clone())\r\n this.uniOffset = new Uniform(this.universal.offset().clone())\r\n this.uniShape = new Uniform(new Vector3(1, 1, 1))\r\n\r\n this.material.onBeforeCompile = (shader) => {\r\n shader.uniforms.time = timeUniform\r\n shader.uniforms.audioLow = lowerUniform\r\n shader.uniforms.audioHigh = upperUniform\r\n shader.uniforms.cage = this.uniCage\r\n shader.uniforms.cageM = this.uniCageM\r\n shader.uniforms.offset = this.uniOffset\r\n shader.uniforms.shape = this.uniShape\r\n shader.uniforms.texmap = new Uniform(tex)\r\n\r\n const addHandUniform =\r\n (dir) =>\r\n ([key, value]) => {\r\n shader.uniforms[`${dir}${key.split('-')[0]}`] = value\r\n }\r\n\r\n Object.entries(left_hand_uniforms).forEach(addHandUniform('left'))\r\n Object.entries(right_hand_uniforms).forEach(addHandUniform('right'))\r\n\r\n shader.vertexShader = shader.vertexShader\r\n .replace('#include ', commonVertChunk)\r\n .replace('#include ', MainVert)\r\n\r\n shader.fragmentShader = shader.fragmentShader\r\n .replace('#include ', fragmentParsChunk)\r\n .replace('vec4 diffuseColor = vec4( diffuse, opacity );', colorChunk)\r\n }\r\n }\r\n\r\n initSystems() {\r\n this.cardinal = sys\r\n .start('cardinal')\r\n .send(\r\n this.past,\r\n this.future,\r\n this.matter,\r\n this.thrust,\r\n this.size,\r\n this.animation,\r\n this.impact,\r\n this.traits,\r\n this.fate.$,\r\n this.universal,\r\n this.cage,\r\n this.velocity,\r\n this.phys,\r\n this.sensed\r\n )\r\n .on((e) => {\r\n const data = e\r\n if (typeof e === 'object') {\r\n e = e.message\r\n }\r\n\r\n switch (e) {\r\n case EMessage.CARD_GATE:\r\n window.location.pathname = `${data.ruler}/${data.realm}`\r\n break\r\n case EMessage.CARD_SEEK:\r\n audio.currentTime = data.time\r\n setTimeout(\r\n () => this.cardinal.postMessage(EMessage.CARD_SEEKED),\r\n 500\r\n )\r\n break\r\n case EMessage.UNI_SCORE:\r\n this.score.set(this.universal.score())\r\n break\r\n case EMessage.LAND_REMOVE:\r\n if (!this.first || !realms[data.id]) return\r\n\r\n realms[data.id].cardinal.postMessage(EMessage.FREE_ALL)\r\n // remove all lands with that id\r\n break\r\n case EMessage.LAND_ADD:\r\n if (!this.first || isQuest || options.$.has('ISOLATE')) return\r\n if (!realms[data.id]) {\r\n realms[data.id] = new Realm()\r\n }\r\n\r\n realms[data.id].universalCage(data.cage)\r\n realms[data.id].universalOffset(data)\r\n realms[data.id].universalShape(data.shape)\r\n\r\n // now to load a timeline\r\n realms[data.id].load(data.ruler, data.land)\r\n break\r\n case EMessage.FAE_ROT_UPDATE:\r\n if (!this.fantasy) return\r\n body.$.rotation.set(\r\n (this.universal.faeRX() / NORMALIZER) * Math.PI * 2,\r\n (this.universal.faeRY() / NORMALIZER) * Math.PI * 2,\r\n (this.universal.faeRZ() / NORMALIZER) * Math.PI * 2\r\n )\r\n\r\n break\r\n case EMessage.CARD_TICK:\r\n const { atoms } = this\r\n atoms.geometry.getAttribute('animation').needsUpdate = true\r\n atoms.geometry.getAttribute('matter').needsUpdate = true\r\n atoms.geometry.getAttribute('size').needsUpdate = true\r\n break\r\n case EMessage.FAE_POS_UPDATE:\r\n if (!this.fantasy) return\r\n body.$.position.copy(\r\n $vec3\r\n .set(\r\n this.universal.faeX(),\r\n this.universal.faeY(),\r\n this.universal.faeZ()\r\n )\r\n .multiplyScalar(0.01)\r\n .add(this.universal.offset().multiplyScalar(0.005))\r\n )\r\n break\r\n case EMessage.CLEAR_COLOR_UPDATE:\r\n if (!this.fantasy) return\r\n renderer.setClearColor(this.universal.clearColor())\r\n break\r\n\r\n case EMessage.CARD_MIDI_CHIRP:\r\n if (!this.fantasy) return\r\n Chirp(...data.data)\r\n break\r\n\r\n case EMessage.CARD_MIDI:\r\n if (!this.fantasy) return\r\n\r\n // @ts-ignore\r\n MIDI(...data.data)\r\n break\r\n }\r\n })\r\n\r\n this.physics = sys\r\n .start('physics')\r\n .send(\r\n this.past,\r\n this.future,\r\n this.matter,\r\n this.thrust,\r\n this.size,\r\n this.impact,\r\n this.universal,\r\n this.cage,\r\n this.velocity,\r\n this.phys,\r\n this.input\r\n )\r\n .bind(this.cardinal)\r\n .on((d) => {\r\n if (d === undefined) return\r\n\r\n switch (typeof d) {\r\n case 'number':\r\n this.cardinal.postMessage(d)\r\n }\r\n })\r\n\r\n this.ai = sys\r\n .start('ai')\r\n .send(\r\n this.future,\r\n this.matter,\r\n this.thrust,\r\n this.size,\r\n this.impact,\r\n this.universal,\r\n this.velocity,\r\n this.traits,\r\n this.phys\r\n )\r\n .bind(this.cardinal)\r\n\r\n this.yggdrasil = new Yggdrasil()\r\n .send(this.future, this.matter, this.size, multiplayer, this.input)\r\n .bind(this.cardinal)\r\n\r\n this.senses = sys\r\n .start('senses')\r\n .send(\r\n this.future,\r\n this.matter,\r\n this.size,\r\n this.universal,\r\n this.traits,\r\n this.sensed,\r\n this.phys,\r\n this.velocity,\r\n this.input\r\n )\r\n .bind(this.cardinal)\r\n .pump(this.cardinal)\r\n }\r\n\r\n universalCage(cage: Box3) {\r\n this.universal.cage(cage)\r\n this.uniCage.value = this.uniCage.value.copy(cage.min)\r\n this.uniCageM.value = this.uniCageM.value.copy(cage.max)\r\n }\r\n\r\n universalOffset(offset: Vector3) {\r\n this.universal.offset(offset)\r\n this.uniOffset.value = this.uniOffset.value.copy(offset)\r\n }\r\n\r\n universalShape(shape: Vector3) {\r\n this.uniShape.value = this.uniShape.value.copy(shape)\r\n }\r\n\r\n initListeners() {\r\n this.cancels.push(\r\n timing.on(($t) => {\r\n this.universal.time(timeUniform.value)\r\n // only need to check if first\r\n if (!this.first && !this.fantasy && this.slowFantasy++ % 10 !== 0)\r\n return\r\n\r\n const { atoms } = this\r\n atoms.geometry.getAttribute('past').needsUpdate = true\r\n atoms.geometry.getAttribute('future').needsUpdate = true\r\n\r\n if (!this.first) return\r\n\r\n if ($t > nextLandCheck) {\r\n first.$.updateFantasy()\r\n nextLandCheck += 1000\r\n }\r\n }),\r\n // update universal\r\n seconds.on(($s) => {\r\n if (this.fantasy) this.universal.musicTime($s)\r\n }),\r\n\r\n this.fate.on(($t) => {\r\n if ($t === undefined) return\r\n\r\n this.cardinal.send(EMessage.FATE_UPDATE)\r\n this.cardinal._queue = []\r\n this.score.set(0)\r\n\r\n if (!this.first) return\r\n\r\n // update the timelineJSON for UI\r\n this.fateJSON.set(this.fate.$.toObject())\r\n }),\r\n this.voxes.on(($voxes) => {\r\n this.cardinal.send($voxes)\r\n })\r\n )\r\n }\r\n\r\n initAtoms() {\r\n this.atoms = new InstancedMesh(\r\n new BoxBufferGeometry(\r\n SIZE * INFRINGEMENT,\r\n SIZE * INFRINGEMENT,\r\n SIZE * INFRINGEMENT,\r\n FACES,\r\n FACES,\r\n FACES\r\n )\r\n .setAttribute(\r\n 'animation',\r\n new InstancedBufferAttribute(this.animation, Animation.COUNT)\r\n )\r\n .setAttribute(\r\n 'past',\r\n new InstancedBufferAttribute(this.past, SpaceTime.COUNT)\r\n )\r\n .setAttribute(\r\n 'future',\r\n new InstancedBufferAttribute(this.future, SpaceTime.COUNT)\r\n )\r\n .setAttribute(\r\n 'matter',\r\n new InstancedBufferAttribute(this.matter, Matter.COUNT)\r\n )\r\n .setAttribute(\r\n 'size',\r\n new InstancedBufferAttribute(this.size, Size.COUNT)\r\n ),\r\n this.material,\r\n ATOM_COUNT\r\n )\r\n\r\n for (let i = 0; i < this.atoms.count; i++) {\r\n this.atoms.setMatrixAt(i, IDENTITY)\r\n }\r\n\r\n const { atoms } = this\r\n atoms.geometry.getAttribute('animation').needsUpdate = true\r\n atoms.geometry.getAttribute('matter').needsUpdate = true\r\n atoms.geometry.getAttribute('size').needsUpdate = true\r\n atoms.geometry.getAttribute('past').needsUpdate = true\r\n atoms.geometry.getAttribute('future').needsUpdate = true\r\n\r\n scene.$.add(this.atoms)\r\n }\r\n\r\n destroy() {\r\n if (!this.cardinal) return\r\n this.cancels.forEach((c) => c())\r\n\r\n this.cardinal.terminate()\r\n this.physics.terminate()\r\n\r\n delete this.cardinal\r\n delete this.physics\r\n\r\n scene.$.remove(this.atoms)\r\n\r\n this.atoms.dispose()\r\n\r\n camera.remove(this.blinder)\r\n\r\n fantasies.$.slice(fantasies.$.indexOf(this), 1)\r\n fantasies.poke()\r\n this.destroyed = true\r\n }\r\n\r\n // load a github repo into this land\r\n async load(ruler: string, realm: string) {\r\n const url = `/github/${ruler}/${realm}`\r\n if (!cache[url]) cache[url] = fetch(url).then((r) => r.arrayBuffer())\r\n const data = await cache[url]\r\n\r\n Load(data, this)\r\n }\r\n\r\n updateFantasy() {\r\n // check bounding box of all lands and update fantasy if needed\r\n let realm: Realm = this\r\n\r\n for (let r of Object.values(realms)) {\r\n $cage.min.copy(r.uniCage.value).multiplyScalar(0.1)\r\n $cage.max.copy(r.uniCageM.value).multiplyScalar(0.1)\r\n\r\n if (\r\n !$cage\r\n .translate(r.uniOffset.value)\r\n .containsPoint($vec3.copy(body.$.position).multiplyScalar(200))\r\n ) {\r\n continue\r\n }\r\n\r\n realm = r\r\n\r\n break\r\n }\r\n\r\n if (fantasy.$ === realm) return\r\n\r\n fantasy.set(realm)\r\n }\r\n\r\n get fantasy() {\r\n return fantasy.$ === this\r\n }\r\n get first() {\r\n return first.$ === this\r\n }\r\n\r\n clearRealms() {\r\n Object.entries(realms).forEach(([k, r]) => {\r\n delete realms[k]\r\n r.destroy()\r\n })\r\n }\r\n}\r\n\r\nfantasy.set(new Realm())\r\n","// move body smoothly\n\nimport { fantasies } from 'src/realm'\nimport { body, camera, renderer } from 'src/render'\nimport { delta, timing } from 'src/shader/time'\nimport { Value } from 'src/value'\nimport { Vector3 } from 'three'\n\nexport const MIN_VELOCITY = 0.3\n\nexport const velocity = new Value(new Vector3(0, 0, 0))\nexport const angular = new Value(0)\n\nexport const walk = new Value()\n\nconst velta = new Vector3()\n\nlet i = 0\ntiming.on(($t) => {\n const l = Math.abs(velocity.$.length())\n if (l > MIN_VELOCITY) {\n velta.copy(velocity.$).multiplyScalar(delta.$)\n\n velocity.$.sub(velta)\n body.$.position.add(\n velta.applyQuaternion(\n renderer.xr.isPresenting ? camera.quaternion : body.$.quaternion\n )\n )\n }\n\n fantasies.$.forEach((f) => {\n f.universal.faeX(body.$.position.x * 2000)\n f.universal.faeY(body.$.position.y * 2000)\n f.universal.faeZ(body.$.position.z * 2000)\n })\n\n if (Math.abs(angular.$) > MIN_VELOCITY) {\n const angleta = angular.$ * delta.$ * 5\n\n body.$.rotateY(angleta)\n\n angular.$ -= angleta\n }\n})\n","import { AXIS, pad_axes } from 'src/input/gamepad'\r\nimport { key_down, key_map, key_up } from 'src/input/keyboard'\r\nimport { mouse_left, mouse_pos, mouse_right } from 'src/input/mouse'\r\nimport { left_controller, right_controller } from 'src/input/phony'\r\nimport { first } from 'src/realm'\r\nimport { body, camera, renderer } from 'src/render'\r\nimport { delta } from 'src/shader/time'\r\nimport { Value } from 'src/value'\r\nimport { MathUtils, Quaternion, Vector2, Vector3 } from 'three'\r\nimport { looking } from './controls'\r\nimport { angular, velocity } from './smooth'\r\n\r\nexport const move_inputs = new Value(new Vector3(0, 0, 0))\r\nexport const fly_engaged = new Value(false)\r\n\r\nconst IDENTITY = new Quaternion().identity()\r\n\r\nconst CAPS_SPEED = 8\r\nconst SPEED = 3\r\n\r\nkey_down.on(($k) => {\r\n switch ($k.toLowerCase()) {\r\n case 'capslock':\r\n fly_engaged.set(!fly_engaged.$)\r\n break\r\n case 'a':\r\n move_inputs.$.x = -SPEED\r\n break\r\n case 'd':\r\n move_inputs.$.x = SPEED\r\n break\r\n case 'w':\r\n move_inputs.$.z = -SPEED\r\n break\r\n case 's':\r\n move_inputs.$.z = SPEED\r\n break\r\n case 'f':\r\n move_inputs.$.y = -SPEED\r\n break\r\n case ' ':\r\n case 'r':\r\n move_inputs.$.y = SPEED\r\n break\r\n }\r\n})\r\n\r\nkey_up.on(($k) => {\r\n switch ($k.toLowerCase()) {\r\n case 'a':\r\n case 'd':\r\n move_inputs.$.x = 0\r\n break\r\n case 'w':\r\n case 's':\r\n move_inputs.$.z = 0\r\n break\r\n case ' ':\r\n case 'r':\r\n case 'f':\r\n move_inputs.$.y = 0\r\n break\r\n }\r\n})\r\n\r\nlet timer\r\n\r\nfunction ClearMouse() {\r\n mouse_left.set(false)\r\n}\r\nlet firstAx = true\r\n// emulate mouse_pos updates\r\npad_axes.on(($axis) => {\r\n if (timer) {\r\n clearTimeout(timer)\r\n timer = undefined\r\n }\r\n\r\n if (firstAx) {\r\n firstAx = false\r\n return\r\n }\r\n\r\n mouse_left.set(true)\r\n timer = setTimeout(ClearMouse, 1000)\r\n\r\n mouse_pos.$.x = $axis[AXIS[2]]\r\n mouse_pos.$.y = -$axis[AXIS[3]]\r\n\r\n move_inputs.$.x = $axis[AXIS[0]] * 5\r\n move_inputs.$.z = $axis[AXIS[1]] * 5\r\n})\r\n\r\nconst $vec3 = new Vector3()\r\nlet camera_mucked = false\r\n\r\nconst LOOK_SPEED = 100\r\nconst targetPosition = new Vector3()\r\nconst vert = new Vector2(0, Math.PI)\r\n\r\nlet lat = 0\r\nlet lon = 0\r\n\r\nfunction UpdateCamera($dt: number) {\r\n camera_mucked = true\r\n lon -= mouse_pos.$.x * $dt * LOOK_SPEED\r\n lat -= (mouse_pos.$.y * $dt * LOOK_SPEED * Math.PI) / (vert.y - vert.x)\r\n\r\n lat = Math.max(-85, Math.min(85, lat))\r\n\r\n targetPosition\r\n .setFromSphericalCoords(\r\n 1,\r\n MathUtils.mapLinear(\r\n MathUtils.degToRad(90 - lat),\r\n 0,\r\n Math.PI,\r\n vert.x,\r\n vert.y\r\n ),\r\n MathUtils.degToRad(lon)\r\n )\r\n .add(body.$.position)\r\n\r\n body.$.lookAt(targetPosition)\r\n\r\n camera.quaternion.slerp(\r\n key_map.$['Shift'] ? body.$.quaternion : IDENTITY,\r\n 0.1\r\n )\r\n}\r\n\r\nconst avg = new Vector3()\r\n\r\nlet vr_mucked = false\r\ndelta.on(($dt) => {\r\n // only run not in VR\r\n if (renderer.xr.isPresenting) {\r\n vr_mucked = true\r\n if (camera_mucked) {\r\n camera_mucked = false\r\n body.$.quaternion.identity()\r\n }\r\n if (right_controller.$) {\r\n const [y, yn, x, z] = right_controller.$.userData.axes\r\n\r\n velocity.$.add($vec3.set(x, y - yn, z).multiplyScalar($dt * 10))\r\n }\r\n\r\n if (left_controller.$) {\r\n const [, x] = left_controller.$.userData.axes\r\n\r\n angular.$ += x * $dt * 100\r\n }\r\n\r\n return\r\n } else if (vr_mucked) {\r\n vr_mucked = false\r\n camera.quaternion.identity()\r\n camera.position.set(0, 0, 0)\r\n }\r\n\r\n if (move_inputs.$.length() !== 0 || mouse_right.$) {\r\n velocity.$.add($vec3.copy(move_inputs.$).multiplyScalar($dt * 3))\r\n }\r\n\r\n if (looking.$) {\r\n UpdateCamera($dt)\r\n }\r\n\r\n const avatar = first.$.universal.avatar()\r\n if (avatar > 0) {\r\n const atom = first.$.future.vec3(avatar).multiplyScalar(0.0005)\r\n atom.y += first.$.size.y(avatar) * 0.0005 + 1\r\n // move us towards the avatar location\r\n const thrust = first.$.universal.thrustStrength()\r\n body.$.position.lerp(\r\n avg\r\n .multiplyScalar(49)\r\n .add(atom)\r\n .multiplyScalar(1 / 50),\r\n $dt * (4 - thrust / 100)\r\n )\r\n\r\n atom.sub(body.$.position).length()\r\n\r\n atom.multiplyScalar(thrust).negate()\r\n\r\n // update velocity of avatar\r\n const { velocity } = first.$\r\n\r\n // wait for them\r\n velocity.addX(avatar, atom.x)\r\n thrust > 100 && velocity.addY(avatar, atom.y)\r\n velocity.addZ(avatar, atom.z)\r\n }\r\n})\r\n","import { IJointGroup, importantBits } from './joints'\n\nexport const poses: { [key: string]: { [idx: string]: number[][] } } = {\n left: {\n // TODO: re-enable was triggered by phony lol\n // Snapshot: [\n // [\n // 0, 0.0597573616411407, 0.05597039264670981, 0.05659221161806142,\n // 0.021291086461522148,\n // ],\n // [\n // 0.0597573616411407, 0, 0.015559802625893696, 0.03424524997676795,\n // 0.07717465219722047,\n // ],\n // [\n // 0.05597039264670981, 0.015559802625893696, 0, 0.018734438937208393,\n // 0.0728145490102279,\n // ],\n // [\n // 0.05659221161806142, 0.03424524997676795, 0.018734438937208393, 0,\n // 0.07180424849921677,\n // ],\n // [\n // 0.021291086461522148, 0.07717465219722047, 0.0728145490102279,\n // 0.07180424849921677, 0,\n // ],\n // ],\n Walk1: [\n [\n 0, 0.09931101870208082, 0.06269222167842753, 0.04643074134550789,\n 0.05090365124847403,\n ],\n [\n 0.09931101870208082, 0, 0.07268531954699639, 0.12048283035341696,\n 0.1332110641202932,\n ],\n [\n 0.06269222167842753, 0.07268531954699639, 0, 0.10505664410267929,\n 0.1112139522386378,\n ],\n [\n 0.04643074134550789, 0.12048283035341696, 0.10505664410267929, 0,\n 0.015214635827136157,\n ],\n [\n 0.05090365124847403, 0.1332110641202932, 0.1112139522386378,\n 0.015214635827136157, 0,\n ],\n ],\n Walk2: [\n [\n 0, 0.04171218309075112, 0.07920973136599817, 0.05787655604752166,\n 0.06450585664602361,\n ],\n [\n 0.04171218309075112, 0, 0.0556840194739412, 0.09048375228223486,\n 0.10127841657273244,\n ],\n [\n 0.07920973136599817, 0.0556840194739412, 0, 0.11234655983701247,\n 0.12594100344180162,\n ],\n [\n 0.05787655604752166, 0.09048375228223486, 0.11234655983701247, 0,\n 0.01710266751195812,\n ],\n [\n 0.06450585664602361, 0.10127841657273244, 0.12594100344180162,\n 0.01710266751195812, 0,\n ],\n ],\n Swipe: [\n [\n 0, 0.03348944442278403, 0.035748379005681535, 0.03766253126328981,\n 0.07367476457590896,\n ],\n [\n 0.03348944442278403, 0, 0.016875792159032975, 0.028888324015462655,\n 0.09961736566114908,\n ],\n [\n 0.035748379005681535, 0.016875792159032975, 0, 0.012697977829023542,\n 0.09437403172482056,\n ],\n [\n 0.03766253126328981, 0.028888324015462655, 0.012697977829023542, 0,\n 0.0894653529824639,\n ],\n [\n 0.07367476457590896, 0.09961736566114908, 0.09437403172482056,\n 0.0894653529824639, 0,\n ],\n ],\n SwipeOther: [\n [\n 0, 0.08078659733628005, 0.09350004202211167, 0.1092196983287712,\n 0.15736197965406235,\n ],\n [\n 0.08078659733628005, 0, 0.014478818843341033, 0.03089512033221984,\n 0.08989434303442337,\n ],\n [\n 0.09350004202211167, 0.014478818843341033, 0, 0.01665959466433419,\n 0.08407039835867264,\n ],\n [\n 0.1092196983287712, 0.03089512033221984, 0.01665959466433419, 0,\n 0.08118890250706919,\n ],\n [\n 0.15736197965406235, 0.08989434303442337, 0.08407039835867264,\n 0.08118890250706919, 0,\n ],\n ],\n },\n right: {\n SwipeOther: [\n [\n 0, 0.07989537672305744, 0.0944663736409222, 0.10848955059295795,\n 0.16266428417447223,\n ],\n [\n 0.07989537672305744, 0, 0.016949359403942873, 0.030458478834395508,\n 0.09859283344773746,\n ],\n [\n 0.0944663736409222, 0.016949359403942873, 0, 0.014086799333727213,\n 0.0938572840771014,\n ],\n [\n 0.10848955059295795, 0.030458478834395508, 0.014086799333727213, 0,\n 0.08876286795459294,\n ],\n [\n 0.16266428417447223, 0.09859283344773746, 0.0938572840771014,\n 0.08876286795459294, 0,\n ],\n ],\n Swipe: [\n [\n 0, 0.04478615622736302, 0.05534348308437427, 0.06606722482815876,\n 0.07590013131440945,\n ],\n [\n 0.04478615622736302, 0, 0.019194501610114566, 0.03517178723604311,\n 0.09859475981154411,\n ],\n [\n 0.05534348308437427, 0.019194501610114566, 0, 0.01618770480789893,\n 0.09512737773038535,\n ],\n [\n 0.06606722482815876, 0.03517178723604311, 0.01618770480789893, 0,\n 0.09515249473764524,\n ],\n [\n 0.07590013131440945, 0.09859475981154411, 0.09512737773038535,\n 0.09515249473764524, 0,\n ],\n ],\n // Snapshot: [\n // [\n // 0, 0.05221198691161727, 0.04325815601736576, 0.03896946691801054,\n // 0.030524475034856113,\n // ],\n // [\n // 0.05221198691161727, 0, 0.015093493997519908, 0.030224405545116555,\n // 0.04243716092474642,\n // ],\n // [\n // 0.04325815601736576, 0.015093493997519908, 0, 0.015283831243613369,\n // 0.02844230686577347,\n // ],\n // [\n // 0.03896946691801054, 0.030224405545116555, 0.015283831243613369, 0,\n // 0.015423297041377513,\n // ],\n // [\n // 0.030524475034856113, 0.04243716092474642, 0.02844230686577347,\n // 0.015423297041377513, 0,\n // ],\n // ],\n Walk1: [\n [\n 0, 0.1079855212494816, 0.08207193276824938, 0.020798567760882188,\n 0.03262642428795622,\n ],\n [\n 0.1079855212494816, 0, 0.06550271568535172, 0.12008870697258106,\n 0.1285862574302885,\n ],\n [\n 0.08207193276824938, 0.06550271568535172, 0, 0.10087662547780488,\n 0.11042267027232314,\n ],\n [\n 0.020798567760882188, 0.12008870697258106, 0.10087662547780488, 0,\n 0.013149101942110662,\n ],\n [\n 0.03262642428795622, 0.1285862574302885, 0.11042267027232314,\n 0.013149101942110662, 0,\n ],\n ],\n Walk2: [\n [\n 0, 0.12017182696685748, 0.0329877597048749, 0.04412158688727415,\n 0.055025740312271,\n ],\n [\n 0.12017182696685748, 0, 0.10847151363909528, 0.1301119907579187,\n 0.14122178513057074,\n ],\n [\n 0.0329877597048749, 0.10847151363909528, 0, 0.027518716388127553,\n 0.04124372590478418,\n ],\n [\n 0.04412158688727415, 0.1301119907579187, 0.027518716388127553, 0,\n 0.013770328951701612,\n ],\n [\n 0.055025740312271, 0.14122178513057074, 0.04124372590478418,\n 0.013770328951701612, 0,\n ],\n ],\n },\n}\n\nexport function poseValue(hand: IJointGroup) {\n return importantBits.map((bit) =>\n importantBits.map((o_bit) =>\n hand.joints[bit].position.distanceTo(hand.joints[o_bit].position)\n )\n )\n}\nexport function snapshotPose(hand: IJointGroup) {\n console.log(hand.handedness, JSON.stringify(poseValue(hand)))\n}\n","import { angular } from 'src/controller/smooth'\nimport { IJointGroup } from 'src/input/joints'\n\nconst SPEED = 0.75\nexport function Swipe(hand: IJointGroup) {\n switch (hand.handedness) {\n case 'left':\n // turn left\n angular.set(angular.$ - SPEED)\n break\n case 'right':\n angular.set(angular.$ + SPEED)\n break\n }\n}\n\nexport function SwipeOther(hand: IJointGroup) {\n switch (hand.handedness) {\n case 'left':\n // turn left\n angular.set(angular.$ + SPEED)\n break\n case 'right':\n angular.set(angular.$ - SPEED)\n break\n }\n}\n","import { velocity } from 'src/controller/smooth'\n\nexport const WALK_SPEED = 0.75\n\nfunction DoWalk() {\n velocity.$.z -= WALK_SPEED\n}\n\nexport const Walk1 = DoWalk\nexport const Walk2 = DoWalk\n","import { IJointGroup } from 'src/input/joints'\nimport { snapshotPose } from 'src/input/poses'\nimport { left_hand, right_hand } from 'src/input/xr'\n\nexport function Snapshot(hand: IJointGroup) {\n // take a snapshot of the opposite hand\n switch (hand.handedness) {\n case 'left':\n snapshotPose(right_hand.$)\n break\n case 'right':\n snapshotPose(left_hand.$)\n break\n }\n}\n","import { MIN_POSE_VALUE } from 'src/config'\nimport { left_controller, right_controller } from 'src/input/phony'\nimport { poses, poseValue } from 'src/input/poses'\nimport { renderer } from 'src/render'\nimport { Timer } from 'src/shader/time'\nimport { VRButton } from 'src/VRButton'\nimport { IJointGroup } from '../input/joints'\nimport { last_pose, pose, VRInit } from '../input/xr'\nimport { Value } from '../value'\nimport { audio } from './audio'\nimport * as Spells from './spell'\n\nrenderer.xr.enabled = true\n\nexport type PoseValues = number[][]\n\nexport const button = VRButton.createButton(renderer, undefined)\n\ndocument.body.appendChild(button)\n\nexport const onVRClick = new Value(false)\n\nTimer(200, () => {\n if (!VRInit.$) return\n\n const session = renderer.xr.getSession()\n let i = 0\n\n if (!session || !session.inputSources) return\n\n for (const source of session.inputSources) {\n switch (true) {\n case source.gamepad !== undefined: {\n const pad = renderer.xr.getController(i)\n pad.userData = source.gamepad\n switch (source.handedness) {\n case 'right':\n left_controller.$ = pad\n break\n default:\n right_controller.$ = pad\n break\n }\n break\n }\n case source.hand !== undefined: {\n const pad = renderer.xr.getHand(i)\n switch (source.handedness) {\n case 'left':\n left_controller.$ = pad\n break\n default:\n right_controller.$ = pad\n break\n }\n break\n }\n }\n\n i++\n }\n})\n\nbutton.addEventListener('click', () => {\n onVRClick.set(true)\n\n audio.play()\n\n VRInit.set(true)\n})\n\n// return the variance from the two posts\nfunction comparePose(p1: PoseValues, p2: PoseValues): number {\n return p1.reduce(\n (sum, arr, j) =>\n arr.reduce((s_sum, v, i) => {\n return s_sum + Math.abs(v - p2[j][i])\n }, sum),\n 0\n )\n}\n\npose.on((v) => requestAnimationFrame(() => last_pose.set(v)))\n\nexport function doPose(hand: IJointGroup) {\n const handPoseValue = poseValue(hand)\n let winner\n let valueToBeat = MIN_POSE_VALUE\n\n // calc hand pose deltas\n Object.entries(poses[hand.handedness]).forEach(([key, value]) => {\n const res = comparePose(value, handPoseValue)\n if (res > valueToBeat) {\n return\n }\n winner = key\n valueToBeat = res\n })\n\n const lastPose = pose.$[hand.handedness]\n if (winner && winner !== lastPose) {\n pose.$[hand.handedness] = winner\n pose.poke()\n\n if (Spells[winner]) {\n Spells[winner](hand)\n }\n\n const k = `Un${lastPose}`\n if (Spells[k]) {\n Spells[k](hand)\n }\n } else if (winner === undefined && lastPose) {\n pose.$[hand.handedness] = ''\n\n const k = `Un${lastPose}`\n if (Spells[k]) {\n Spells[k](hand)\n }\n }\n}\n","class VRButton {\n static createButton(renderer, options) {\n if (options) {\n console.error(\n 'THREE.VRButton: The \"options\" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.'\n )\n }\n\n const button = document.createElement('button')\n\n function showEnterVR(/*device*/) {\n let currentSession = null\n\n async function onSessionStarted(session) {\n session.addEventListener('end', onSessionEnded)\n // @ts-ignore\n renderer.xr.setReferenceSpaceType('local')\n await renderer.xr.setSession(session)\n button.textContent = 'EXIT VR'\n\n currentSession = session\n }\n\n function onSessionEnded(/*event*/) {\n currentSession.removeEventListener('end', onSessionEnded)\n\n button.textContent = 'ENTER VR'\n\n currentSession = null\n }\n\n //\n\n button.style.display = ''\n\n button.style.cursor = 'pointer'\n button.style.left = 'calc(50% - 50px)'\n button.style.width = '100px'\n\n button.textContent = 'ENTER VR'\n\n button.onmouseenter = function () {\n button.style.opacity = '1.0'\n }\n\n button.onmouseleave = function () {\n button.style.opacity = '0.5'\n }\n\n button.onclick = function () {\n if (currentSession === null) {\n // WebXR's requestReferenceSpace only works if the corresponding feature\n // was requested at session creation time. For simplicity, just ask for\n // the interesting ones as optional features, but be aware that the\n // requestReferenceSpace call will fail if it turns out to be unavailable.\n // ('local' is always available for immersive sessions and doesn't need to\n // be requested separately.)\n\n const sessionInit = {\n optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking'],\n }\n\n // @ts-ignore\n navigator.xr\n .requestSession('immersive-vr', sessionInit)\n .then(onSessionStarted)\n } else {\n currentSession.end()\n }\n }\n }\n\n function disableButton() {\n button.style.display = 'none;'\n\n button.style.cursor = 'auto'\n button.style.left = 'calc(50% - 75px)'\n button.style.width = '150px'\n\n button.onmouseenter = null\n button.onmouseleave = null\n\n button.onclick = null\n }\n\n function showWebXRNotFound() {\n disableButton()\n\n button.textContent = 'VR'\n }\n\n function stylizeElement(element) {\n element.style.position = 'absolute'\n element.style.bottom = '20px'\n element.style.padding = '12px 6px'\n element.style.border = '1px solid #fff'\n element.style.borderRadius = '4px'\n element.style.background = 'rgba(0,0,0,0.1)'\n element.style.color = '#fff'\n element.style.font = 'normal 13px sans-serif'\n element.style.textAlign = 'center'\n element.style.opacity = '0.5'\n element.style.outline = 'none'\n element.style.zIndex = '999'\n }\n\n if ('xr' in navigator) {\n button.id = 'VRButton'\n button.style.display = 'none'\n\n stylizeElement(button)\n\n // @ts-ignore\n navigator.xr\n .isSessionSupported('immersive-vr')\n .then(function (supported) {\n supported ? showEnterVR() : showWebXRNotFound()\n })\n\n return button\n } else {\n const message = document.createElement('a')\n\n if (window.isSecureContext === false) {\n message.href = document.location.href.replace(/^http:/, 'https:')\n message.innerHTML = 'WEBXR NEEDS HTTPS' // TODO Improve message\n } else {\n message.href = 'https://immersiveweb.dev/'\n message.innerHTML = 'WEBXR NOT AVAILABLE'\n }\n\n message.style.left = 'calc(50% - 90px)'\n message.style.width = '180px'\n message.style.textDecoration = 'none'\n\n stylizeElement(message)\n\n return message\n }\n }\n}\n\nexport { VRButton }\n","import { EAnimation } from 'src/buffer/animation'\nimport { EPhase } from 'src/buffer/phys'\nimport { NORMALIZER } from 'src/config'\nimport { doPose } from 'src/controller/hands'\nimport { vr_keys } from 'src/input/joints'\nimport { hands, left_hand_uniforms, right_hand_uniforms } from 'src/input/xr'\nimport { first } from 'src/realm'\nimport { body } from 'src/render'\nimport { timing } from 'src/shader/time'\nimport { EMessage } from 'src/system/enum'\nimport { SystemWorker } from 'src/system/sys'\nimport { Color, Vector3 } from 'three'\n\nlet hand_joints: number[] = []\nconst $vec = new Vector3()\n\n// Rezes allow us to inject into the worker simulation from the main thread\nexport function RezHands(cardinal: SystemWorker) {\n hand_joints = []\n\n // request the hands\n // vr_keys is an enum and therefore 2x the length, which is what we want\n // for two hands anyhow\n for (let i = 0; i < Object.keys(vr_keys).length; i++) {\n cardinal.send(EMessage.REZ)\n cardinal.waitForEntity((id) => {\n hand_joints.push(id)\n })\n }\n}\n\nconst rTip = /tip$/\nconst rMeta = /metacarpal$|proximal$/\n\nconst $color = new Color()\n\n// update hand rezes if they exist\ntiming.on(() => {\n // no hands, nothing to do\n\n if (hands.$.length === 0) {\n RezHands(first.$.cardinal)\n return\n }\n const {\n size,\n future,\n phys,\n matter,\n past,\n animation: animation,\n cage,\n universal,\n } = first.$\n\n let gid\n let tipi = 0\n for (let i = 0; i < hand_joints.length; i++) {\n const ix = i % 25\n const iy = Math.floor(i / 25)\n const id = hand_joints[i]\n\n const j = hands.$[iy]?.joints[vr_keys[ix]]\n\n if (ix == 0) gid = j\n\n if (!j) continue\n\n if (ix === 0) {\n doPose(hands.$[iy])\n }\n\n $vec\n .copy(j.position)\n .applyQuaternion(body.$.quaternion)\n .add(body.$.position)\n .multiplyScalar(2)\n\n if (rTip.test(vr_keys[ix])) {\n let target\n // copy hand pos to the uniforms\n switch (hands.$[iy].handedness) {\n case 'left':\n target = left_hand_uniforms\n break\n case 'right':\n target = right_hand_uniforms\n }\n\n target[vr_keys[ix]].value.copy($vec)\n $vec.multiplyScalar(1000)\n universal.faeHand(tipi++, id)\n } else {\n $vec.multiplyScalar(1000)\n }\n\n let s = Math.floor(rMeta.test(vr_keys[ix]) ? 8 : 5) * 9.5\n if (vr_keys[ix].indexOf('meta') !== -1) {\n s *= 0.75\n }\n\n animation.store(id, EAnimation.OFF)\n size.x(id, s)\n size.y(id, s)\n size.z(id, s)\n\n phys.phase(id, EPhase.DIVINE)\n phys.core(id, gid)\n const vari = universal.faeHueVariance() / NORMALIZER\n const universalColor = $color.set(universal.faeHue())\n\n // matter.red(id, NORMALIZER - (Math.random() * NORMALIZER) / 5)\n\n switch (true) {\n case vr_keys[ix] === 'wrist':\n matter.red(id, NORMALIZER)\n matter.green(id, NORMALIZER)\n matter.blue(id, NORMALIZER)\n break\n\n case vr_keys[ix].indexOf('tip') !== -1 ||\n vr_keys[ix].indexOf('thumb') !== -1:\n matter.red(id, Math.min(1, universalColor.r * 1.15) * NORMALIZER)\n matter.green(id, Math.min(1, universalColor.g * 1.15) * NORMALIZER)\n matter.blue(id, Math.min(1, universalColor.b * 1.5) * NORMALIZER)\n break\n default:\n matter.red(\n id,\n Math.round(universalColor.r * NORMALIZER * (1 - Math.random() * vari))\n )\n matter.green(\n id,\n Math.round(universalColor.g * NORMALIZER * (1 - Math.random() * vari))\n )\n matter.blue(\n id,\n Math.round(universalColor.b * NORMALIZER * (1 - Math.random() * vari))\n )\n }\n\n future.x(id, Math.floor($vec.x))\n future.y(id, Math.floor($vec.y))\n future.z(id, Math.floor($vec.z))\n\n past.x(id, future.x(id))\n past.y(id, future.y(id))\n past.z(id, future.z(id))\n past.time(id, Math.floor(timing.$))\n future.time(id, Math.floor(timing.$ + 200))\n cage.mX(id, 0)\n cage.mY(id, 0)\n cage.mZ(id, 0)\n cage.x(id, 0)\n cage.y(id, 0)\n cage.z(id, 0)\n\n // if it has collisions and we're grabbing attach it\n }\n\n // move fae based on hand attachment\n})\n\nlet cancel\nfirst.on(($r) => {\n if (cancel) cancel()\n\n cancel = $r.cardinal.onCancel((e) => {\n // Rez the player hands\n switch (e) {\n case EMessage.FATE_UPDATE:\n RezHands($r.cardinal)\n break\n }\n })\n})\n","function noop() { }\nconst identity = x => x;\nfunction assign(tar, src) {\n // @ts-ignore\n for (const k in src)\n tar[k] = src[k];\n return tar;\n}\nfunction is_promise(value) {\n return value && typeof value === 'object' && typeof value.then === 'function';\n}\nfunction add_location(element, file, line, column, char) {\n element.__svelte_meta = {\n loc: { file, line, column, char }\n };\n}\nfunction run(fn) {\n return fn();\n}\nfunction blank_object() {\n return Object.create(null);\n}\nfunction run_all(fns) {\n fns.forEach(run);\n}\nfunction is_function(thing) {\n return typeof thing === 'function';\n}\nfunction safe_not_equal(a, b) {\n return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}\nlet src_url_equal_anchor;\nfunction src_url_equal(element_src, url) {\n if (!src_url_equal_anchor) {\n src_url_equal_anchor = document.createElement('a');\n }\n src_url_equal_anchor.href = url;\n return element_src === src_url_equal_anchor.href;\n}\nfunction not_equal(a, b) {\n return a != a ? b == b : a !== b;\n}\nfunction is_empty(obj) {\n return Object.keys(obj).length === 0;\n}\nfunction validate_store(store, name) {\n if (store != null && typeof store.subscribe !== 'function') {\n throw new Error(`'${name}' is not a store with a 'subscribe' method`);\n }\n}\nfunction subscribe(store, ...callbacks) {\n if (store == null) {\n return noop;\n }\n const unsub = store.subscribe(...callbacks);\n return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;\n}\nfunction get_store_value(store) {\n let value;\n subscribe(store, _ => value = _)();\n return value;\n}\nfunction component_subscribe(component, store, callback) {\n component.$$.on_destroy.push(subscribe(store, callback));\n}\nfunction create_slot(definition, ctx, $$scope, fn) {\n if (definition) {\n const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);\n return definition[0](slot_ctx);\n }\n}\nfunction get_slot_context(definition, ctx, $$scope, fn) {\n return definition[1] && fn\n ? assign($$scope.ctx.slice(), definition[1](fn(ctx)))\n : $$scope.ctx;\n}\nfunction get_slot_changes(definition, $$scope, dirty, fn) {\n if (definition[2] && fn) {\n const lets = definition[2](fn(dirty));\n if ($$scope.dirty === undefined) {\n return lets;\n }\n if (typeof lets === 'object') {\n const merged = [];\n const len = Math.max($$scope.dirty.length, lets.length);\n for (let i = 0; i < len; i += 1) {\n merged[i] = $$scope.dirty[i] | lets[i];\n }\n return merged;\n }\n return $$scope.dirty | lets;\n }\n return $$scope.dirty;\n}\nfunction update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {\n if (slot_changes) {\n const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);\n slot.p(slot_context, slot_changes);\n }\n}\nfunction update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {\n const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);\n update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn);\n}\nfunction get_all_dirty_from_scope($$scope) {\n if ($$scope.ctx.length > 32) {\n const dirty = [];\n const length = $$scope.ctx.length / 32;\n for (let i = 0; i < length; i++) {\n dirty[i] = -1;\n }\n return dirty;\n }\n return -1;\n}\nfunction exclude_internal_props(props) {\n const result = {};\n for (const k in props)\n if (k[0] !== '$')\n result[k] = props[k];\n return result;\n}\nfunction compute_rest_props(props, keys) {\n const rest = {};\n keys = new Set(keys);\n for (const k in props)\n if (!keys.has(k) && k[0] !== '$')\n rest[k] = props[k];\n return rest;\n}\nfunction compute_slots(slots) {\n const result = {};\n for (const key in slots) {\n result[key] = true;\n }\n return result;\n}\nfunction once(fn) {\n let ran = false;\n return function (...args) {\n if (ran)\n return;\n ran = true;\n fn.call(this, ...args);\n };\n}\nfunction null_to_empty(value) {\n return value == null ? '' : value;\n}\nfunction set_store_value(store, ret, value) {\n store.set(value);\n return ret;\n}\nconst has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);\nfunction action_destroyer(action_result) {\n return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;\n}\n\nconst is_client = typeof window !== 'undefined';\nlet now = is_client\n ? () => window.performance.now()\n : () => Date.now();\nlet raf = is_client ? cb => requestAnimationFrame(cb) : noop;\n// used internally for testing\nfunction set_now(fn) {\n now = fn;\n}\nfunction set_raf(fn) {\n raf = fn;\n}\n\nconst tasks = new Set();\nfunction run_tasks(now) {\n tasks.forEach(task => {\n if (!task.c(now)) {\n tasks.delete(task);\n task.f();\n }\n });\n if (tasks.size !== 0)\n raf(run_tasks);\n}\n/**\n * For testing purposes only!\n */\nfunction clear_loops() {\n tasks.clear();\n}\n/**\n * Creates a new task that runs on each raf frame\n * until it returns a falsy value or is aborted\n */\nfunction loop(callback) {\n let task;\n if (tasks.size === 0)\n raf(run_tasks);\n return {\n promise: new Promise(fulfill => {\n tasks.add(task = { c: callback, f: fulfill });\n }),\n abort() {\n tasks.delete(task);\n }\n };\n}\n\n// Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM\n// at the end of hydration without touching the remaining nodes.\nlet is_hydrating = false;\nfunction start_hydrating() {\n is_hydrating = true;\n}\nfunction end_hydrating() {\n is_hydrating = false;\n}\nfunction upper_bound(low, high, key, value) {\n // Return first index of value larger than input value in the range [low, high)\n while (low < high) {\n const mid = low + ((high - low) >> 1);\n if (key(mid) <= value) {\n low = mid + 1;\n }\n else {\n high = mid;\n }\n }\n return low;\n}\nfunction init_hydrate(target) {\n if (target.hydrate_init)\n return;\n target.hydrate_init = true;\n // We know that all children have claim_order values since the unclaimed have been detached if target is not \n let children = target.childNodes;\n // If target is , there may be children without claim_order\n if (target.nodeName === 'HEAD') {\n const myChildren = [];\n for (let i = 0; i < children.length; i++) {\n const node = children[i];\n if (node.claim_order !== undefined) {\n myChildren.push(node);\n }\n }\n children = myChildren;\n }\n /*\n * Reorder claimed children optimally.\n * We can reorder claimed children optimally by finding the longest subsequence of\n * nodes that are already claimed in order and only moving the rest. The longest\n * subsequence subsequence of nodes that are claimed in order can be found by\n * computing the longest increasing subsequence of .claim_order values.\n *\n * This algorithm is optimal in generating the least amount of reorder operations\n * possible.\n *\n * Proof:\n * We know that, given a set of reordering operations, the nodes that do not move\n * always form an increasing subsequence, since they do not move among each other\n * meaning that they must be already ordered among each other. Thus, the maximal\n * set of nodes that do not move form a longest increasing subsequence.\n */\n // Compute longest increasing subsequence\n // m: subsequence length j => index k of smallest value that ends an increasing subsequence of length j\n const m = new Int32Array(children.length + 1);\n // Predecessor indices + 1\n const p = new Int32Array(children.length);\n m[0] = -1;\n let longest = 0;\n for (let i = 0; i < children.length; i++) {\n const current = children[i].claim_order;\n // Find the largest subsequence length such that it ends in a value less than our current value\n // upper_bound returns first greater value, so we subtract one\n // with fast path for when we are on the current longest subsequence\n const seqLen = ((longest > 0 && children[m[longest]].claim_order <= current) ? longest + 1 : upper_bound(1, longest, idx => children[m[idx]].claim_order, current)) - 1;\n p[i] = m[seqLen] + 1;\n const newLen = seqLen + 1;\n // We can guarantee that current is the smallest value. Otherwise, we would have generated a longer sequence.\n m[newLen] = i;\n longest = Math.max(newLen, longest);\n }\n // The longest increasing subsequence of nodes (initially reversed)\n const lis = [];\n // The rest of the nodes, nodes that will be moved\n const toMove = [];\n let last = children.length - 1;\n for (let cur = m[longest] + 1; cur != 0; cur = p[cur - 1]) {\n lis.push(children[cur - 1]);\n for (; last >= cur; last--) {\n toMove.push(children[last]);\n }\n last--;\n }\n for (; last >= 0; last--) {\n toMove.push(children[last]);\n }\n lis.reverse();\n // We sort the nodes being moved to guarantee that their insertion order matches the claim order\n toMove.sort((a, b) => a.claim_order - b.claim_order);\n // Finally, we move the nodes\n for (let i = 0, j = 0; i < toMove.length; i++) {\n while (j < lis.length && toMove[i].claim_order >= lis[j].claim_order) {\n j++;\n }\n const anchor = j < lis.length ? lis[j] : null;\n target.insertBefore(toMove[i], anchor);\n }\n}\nfunction append(target, node) {\n target.appendChild(node);\n}\nfunction append_styles(target, style_sheet_id, styles) {\n const append_styles_to = get_root_for_style(target);\n if (!append_styles_to.getElementById(style_sheet_id)) {\n const style = element('style');\n style.id = style_sheet_id;\n style.textContent = styles;\n append_stylesheet(append_styles_to, style);\n }\n}\nfunction get_root_for_style(node) {\n if (!node)\n return document;\n const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;\n if (root.host) {\n return root;\n }\n return document;\n}\nfunction append_empty_stylesheet(node) {\n const style_element = element('style');\n append_stylesheet(get_root_for_style(node), style_element);\n return style_element;\n}\nfunction append_stylesheet(node, style) {\n append(node.head || node, style);\n}\nfunction append_hydration(target, node) {\n if (is_hydrating) {\n init_hydrate(target);\n if ((target.actual_end_child === undefined) || ((target.actual_end_child !== null) && (target.actual_end_child.parentElement !== target))) {\n target.actual_end_child = target.firstChild;\n }\n // Skip nodes of undefined ordering\n while ((target.actual_end_child !== null) && (target.actual_end_child.claim_order === undefined)) {\n target.actual_end_child = target.actual_end_child.nextSibling;\n }\n if (node !== target.actual_end_child) {\n // We only insert if the ordering of this node should be modified or the parent node is not target\n if (node.claim_order !== undefined || node.parentNode !== target) {\n target.insertBefore(node, target.actual_end_child);\n }\n }\n else {\n target.actual_end_child = node.nextSibling;\n }\n }\n else if (node.parentNode !== target || node.nextSibling !== null) {\n target.appendChild(node);\n }\n}\nfunction insert(target, node, anchor) {\n target.insertBefore(node, anchor || null);\n}\nfunction insert_hydration(target, node, anchor) {\n if (is_hydrating && !anchor) {\n append_hydration(target, node);\n }\n else if (node.parentNode !== target || node.nextSibling != anchor) {\n target.insertBefore(node, anchor || null);\n }\n}\nfunction detach(node) {\n node.parentNode.removeChild(node);\n}\nfunction destroy_each(iterations, detaching) {\n for (let i = 0; i < iterations.length; i += 1) {\n if (iterations[i])\n iterations[i].d(detaching);\n }\n}\nfunction element(name) {\n return document.createElement(name);\n}\nfunction element_is(name, is) {\n return document.createElement(name, { is });\n}\nfunction object_without_properties(obj, exclude) {\n const target = {};\n for (const k in obj) {\n if (has_prop(obj, k)\n // @ts-ignore\n && exclude.indexOf(k) === -1) {\n // @ts-ignore\n target[k] = obj[k];\n }\n }\n return target;\n}\nfunction svg_element(name) {\n return document.createElementNS('http://www.w3.org/2000/svg', name);\n}\nfunction text(data) {\n return document.createTextNode(data);\n}\nfunction space() {\n return text(' ');\n}\nfunction empty() {\n return text('');\n}\nfunction listen(node, event, handler, options) {\n node.addEventListener(event, handler, options);\n return () => node.removeEventListener(event, handler, options);\n}\nfunction prevent_default(fn) {\n return function (event) {\n event.preventDefault();\n // @ts-ignore\n return fn.call(this, event);\n };\n}\nfunction stop_propagation(fn) {\n return function (event) {\n event.stopPropagation();\n // @ts-ignore\n return fn.call(this, event);\n };\n}\nfunction self(fn) {\n return function (event) {\n // @ts-ignore\n if (event.target === this)\n fn.call(this, event);\n };\n}\nfunction trusted(fn) {\n return function (event) {\n // @ts-ignore\n if (event.isTrusted)\n fn.call(this, event);\n };\n}\nfunction attr(node, attribute, value) {\n if (value == null)\n node.removeAttribute(attribute);\n else if (node.getAttribute(attribute) !== value)\n node.setAttribute(attribute, value);\n}\nfunction set_attributes(node, attributes) {\n // @ts-ignore\n const descriptors = Object.getOwnPropertyDescriptors(node.__proto__);\n for (const key in attributes) {\n if (attributes[key] == null) {\n node.removeAttribute(key);\n }\n else if (key === 'style') {\n node.style.cssText = attributes[key];\n }\n else if (key === '__value') {\n node.value = node[key] = attributes[key];\n }\n else if (descriptors[key] && descriptors[key].set) {\n node[key] = attributes[key];\n }\n else {\n attr(node, key, attributes[key]);\n }\n }\n}\nfunction set_svg_attributes(node, attributes) {\n for (const key in attributes) {\n attr(node, key, attributes[key]);\n }\n}\nfunction set_custom_element_data(node, prop, value) {\n if (prop in node) {\n node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;\n }\n else {\n attr(node, prop, value);\n }\n}\nfunction xlink_attr(node, attribute, value) {\n node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);\n}\nfunction get_binding_group_value(group, __value, checked) {\n const value = new Set();\n for (let i = 0; i < group.length; i += 1) {\n if (group[i].checked)\n value.add(group[i].__value);\n }\n if (!checked) {\n value.delete(__value);\n }\n return Array.from(value);\n}\nfunction to_number(value) {\n return value === '' ? null : +value;\n}\nfunction time_ranges_to_array(ranges) {\n const array = [];\n for (let i = 0; i < ranges.length; i += 1) {\n array.push({ start: ranges.start(i), end: ranges.end(i) });\n }\n return array;\n}\nfunction children(element) {\n return Array.from(element.childNodes);\n}\nfunction init_claim_info(nodes) {\n if (nodes.claim_info === undefined) {\n nodes.claim_info = { last_index: 0, total_claimed: 0 };\n }\n}\nfunction claim_node(nodes, predicate, processNode, createNode, dontUpdateLastIndex = false) {\n // Try to find nodes in an order such that we lengthen the longest increasing subsequence\n init_claim_info(nodes);\n const resultNode = (() => {\n // We first try to find an element after the previous one\n for (let i = nodes.claim_info.last_index; i < nodes.length; i++) {\n const node = nodes[i];\n if (predicate(node)) {\n const replacement = processNode(node);\n if (replacement === undefined) {\n nodes.splice(i, 1);\n }\n else {\n nodes[i] = replacement;\n }\n if (!dontUpdateLastIndex) {\n nodes.claim_info.last_index = i;\n }\n return node;\n }\n }\n // Otherwise, we try to find one before\n // We iterate in reverse so that we don't go too far back\n for (let i = nodes.claim_info.last_index - 1; i >= 0; i--) {\n const node = nodes[i];\n if (predicate(node)) {\n const replacement = processNode(node);\n if (replacement === undefined) {\n nodes.splice(i, 1);\n }\n else {\n nodes[i] = replacement;\n }\n if (!dontUpdateLastIndex) {\n nodes.claim_info.last_index = i;\n }\n else if (replacement === undefined) {\n // Since we spliced before the last_index, we decrease it\n nodes.claim_info.last_index--;\n }\n return node;\n }\n }\n // If we can't find any matching node, we create a new one\n return createNode();\n })();\n resultNode.claim_order = nodes.claim_info.total_claimed;\n nodes.claim_info.total_claimed += 1;\n return resultNode;\n}\nfunction claim_element(nodes, name, attributes, svg) {\n return claim_node(nodes, (node) => node.nodeName === name, (node) => {\n const remove = [];\n for (let j = 0; j < node.attributes.length; j++) {\n const attribute = node.attributes[j];\n if (!attributes[attribute.name]) {\n remove.push(attribute.name);\n }\n }\n remove.forEach(v => node.removeAttribute(v));\n return undefined;\n }, () => svg ? svg_element(name) : element(name));\n}\nfunction claim_text(nodes, data) {\n return claim_node(nodes, (node) => node.nodeType === 3, (node) => {\n const dataStr = '' + data;\n if (node.data.startsWith(dataStr)) {\n if (node.data.length !== dataStr.length) {\n return node.splitText(dataStr.length);\n }\n }\n else {\n node.data = dataStr;\n }\n }, () => text(data), true // Text nodes should not update last index since it is likely not worth it to eliminate an increasing subsequence of actual elements\n );\n}\nfunction claim_space(nodes) {\n return claim_text(nodes, ' ');\n}\nfunction find_comment(nodes, text, start) {\n for (let i = start; i < nodes.length; i += 1) {\n const node = nodes[i];\n if (node.nodeType === 8 /* comment node */ && node.textContent.trim() === text) {\n return i;\n }\n }\n return nodes.length;\n}\nfunction claim_html_tag(nodes) {\n // find html opening tag\n const start_index = find_comment(nodes, 'HTML_TAG_START', 0);\n const end_index = find_comment(nodes, 'HTML_TAG_END', start_index);\n if (start_index === end_index) {\n return new HtmlTagHydration();\n }\n init_claim_info(nodes);\n const html_tag_nodes = nodes.splice(start_index, end_index + 1);\n detach(html_tag_nodes[0]);\n detach(html_tag_nodes[html_tag_nodes.length - 1]);\n const claimed_nodes = html_tag_nodes.slice(1, html_tag_nodes.length - 1);\n for (const n of claimed_nodes) {\n n.claim_order = nodes.claim_info.total_claimed;\n nodes.claim_info.total_claimed += 1;\n }\n return new HtmlTagHydration(claimed_nodes);\n}\nfunction set_data(text, data) {\n data = '' + data;\n if (text.wholeText !== data)\n text.data = data;\n}\nfunction set_input_value(input, value) {\n input.value = value == null ? '' : value;\n}\nfunction set_input_type(input, type) {\n try {\n input.type = type;\n }\n catch (e) {\n // do nothing\n }\n}\nfunction set_style(node, key, value, important) {\n node.style.setProperty(key, value, important ? 'important' : '');\n}\nfunction select_option(select, value) {\n for (let i = 0; i < select.options.length; i += 1) {\n const option = select.options[i];\n if (option.__value === value) {\n option.selected = true;\n return;\n }\n }\n}\nfunction select_options(select, value) {\n for (let i = 0; i < select.options.length; i += 1) {\n const option = select.options[i];\n option.selected = ~value.indexOf(option.__value);\n }\n}\nfunction select_value(select) {\n const selected_option = select.querySelector(':checked') || select.options[0];\n return selected_option && selected_option.__value;\n}\nfunction select_multiple_value(select) {\n return [].map.call(select.querySelectorAll(':checked'), option => option.__value);\n}\n// unfortunately this can't be a constant as that wouldn't be tree-shakeable\n// so we cache the result instead\nlet crossorigin;\nfunction is_crossorigin() {\n if (crossorigin === undefined) {\n crossorigin = false;\n try {\n if (typeof window !== 'undefined' && window.parent) {\n void window.parent.document;\n }\n }\n catch (error) {\n crossorigin = true;\n }\n }\n return crossorigin;\n}\nfunction add_resize_listener(node, fn) {\n const computed_style = getComputedStyle(node);\n if (computed_style.position === 'static') {\n node.style.position = 'relative';\n }\n const iframe = element('iframe');\n iframe.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ' +\n 'overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: -1;');\n iframe.setAttribute('aria-hidden', 'true');\n iframe.tabIndex = -1;\n const crossorigin = is_crossorigin();\n let unsubscribe;\n if (crossorigin) {\n iframe.src = \"data:text/html,\";\n unsubscribe = listen(window, 'message', (event) => {\n if (event.source === iframe.contentWindow)\n fn();\n });\n }\n else {\n iframe.src = 'about:blank';\n iframe.onload = () => {\n unsubscribe = listen(iframe.contentWindow, 'resize', fn);\n };\n }\n append(node, iframe);\n return () => {\n if (crossorigin) {\n unsubscribe();\n }\n else if (unsubscribe && iframe.contentWindow) {\n unsubscribe();\n }\n detach(iframe);\n };\n}\nfunction toggle_class(element, name, toggle) {\n element.classList[toggle ? 'add' : 'remove'](name);\n}\nfunction custom_event(type, detail, bubbles = false) {\n const e = document.createEvent('CustomEvent');\n e.initCustomEvent(type, bubbles, false, detail);\n return e;\n}\nfunction query_selector_all(selector, parent = document.body) {\n return Array.from(parent.querySelectorAll(selector));\n}\nclass HtmlTag {\n constructor() {\n this.e = this.n = null;\n }\n c(html) {\n this.h(html);\n }\n m(html, target, anchor = null) {\n if (!this.e) {\n this.e = element(target.nodeName);\n this.t = target;\n this.c(html);\n }\n this.i(anchor);\n }\n h(html) {\n this.e.innerHTML = html;\n this.n = Array.from(this.e.childNodes);\n }\n i(anchor) {\n for (let i = 0; i < this.n.length; i += 1) {\n insert(this.t, this.n[i], anchor);\n }\n }\n p(html) {\n this.d();\n this.h(html);\n this.i(this.a);\n }\n d() {\n this.n.forEach(detach);\n }\n}\nclass HtmlTagHydration extends HtmlTag {\n constructor(claimed_nodes) {\n super();\n this.e = this.n = null;\n this.l = claimed_nodes;\n }\n c(html) {\n if (this.l) {\n this.n = this.l;\n }\n else {\n super.c(html);\n }\n }\n i(anchor) {\n for (let i = 0; i < this.n.length; i += 1) {\n insert_hydration(this.t, this.n[i], anchor);\n }\n }\n}\nfunction attribute_to_object(attributes) {\n const result = {};\n for (const attribute of attributes) {\n result[attribute.name] = attribute.value;\n }\n return result;\n}\nfunction get_custom_elements_slots(element) {\n const result = {};\n element.childNodes.forEach((node) => {\n result[node.slot || 'default'] = true;\n });\n return result;\n}\n\nconst active_docs = new Set();\nlet active = 0;\n// https://github.com/darkskyapp/string-hash/blob/master/index.js\nfunction hash(str) {\n let hash = 5381;\n let i = str.length;\n while (i--)\n hash = ((hash << 5) - hash) ^ str.charCodeAt(i);\n return hash >>> 0;\n}\nfunction create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {\n const step = 16.666 / duration;\n let keyframes = '{\\n';\n for (let p = 0; p <= 1; p += step) {\n const t = a + (b - a) * ease(p);\n keyframes += p * 100 + `%{${fn(t, 1 - t)}}\\n`;\n }\n const rule = keyframes + `100% {${fn(b, 1 - b)}}\\n}`;\n const name = `__svelte_${hash(rule)}_${uid}`;\n const doc = get_root_for_style(node);\n active_docs.add(doc);\n const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet);\n const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});\n if (!current_rules[name]) {\n current_rules[name] = true;\n stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);\n }\n const animation = node.style.animation || '';\n node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`;\n active += 1;\n return name;\n}\nfunction delete_rule(node, name) {\n const previous = (node.style.animation || '').split(', ');\n const next = previous.filter(name\n ? anim => anim.indexOf(name) < 0 // remove specific animation\n : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations\n );\n const deleted = previous.length - next.length;\n if (deleted) {\n node.style.animation = next.join(', ');\n active -= deleted;\n if (!active)\n clear_rules();\n }\n}\nfunction clear_rules() {\n raf(() => {\n if (active)\n return;\n active_docs.forEach(doc => {\n const stylesheet = doc.__svelte_stylesheet;\n let i = stylesheet.cssRules.length;\n while (i--)\n stylesheet.deleteRule(i);\n doc.__svelte_rules = {};\n });\n active_docs.clear();\n });\n}\n\nfunction create_animation(node, from, fn, params) {\n if (!from)\n return noop;\n const to = node.getBoundingClientRect();\n if (from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom)\n return noop;\n const { delay = 0, duration = 300, easing = identity, \n // @ts-ignore todo: should this be separated from destructuring? Or start/end added to public api and documentation?\n start: start_time = now() + delay, \n // @ts-ignore todo:\n end = start_time + duration, tick = noop, css } = fn(node, { from, to }, params);\n let running = true;\n let started = false;\n let name;\n function start() {\n if (css) {\n name = create_rule(node, 0, 1, duration, delay, easing, css);\n }\n if (!delay) {\n started = true;\n }\n }\n function stop() {\n if (css)\n delete_rule(node, name);\n running = false;\n }\n loop(now => {\n if (!started && now >= start_time) {\n started = true;\n }\n if (started && now >= end) {\n tick(1, 0);\n stop();\n }\n if (!running) {\n return false;\n }\n if (started) {\n const p = now - start_time;\n const t = 0 + 1 * easing(p / duration);\n tick(t, 1 - t);\n }\n return true;\n });\n start();\n tick(0, 1);\n return stop;\n}\nfunction fix_position(node) {\n const style = getComputedStyle(node);\n if (style.position !== 'absolute' && style.position !== 'fixed') {\n const { width, height } = style;\n const a = node.getBoundingClientRect();\n node.style.position = 'absolute';\n node.style.width = width;\n node.style.height = height;\n add_transform(node, a);\n }\n}\nfunction add_transform(node, a) {\n const b = node.getBoundingClientRect();\n if (a.left !== b.left || a.top !== b.top) {\n const style = getComputedStyle(node);\n const transform = style.transform === 'none' ? '' : style.transform;\n node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`;\n }\n}\n\nlet current_component;\nfunction set_current_component(component) {\n current_component = component;\n}\nfunction get_current_component() {\n if (!current_component)\n throw new Error('Function called outside component initialization');\n return current_component;\n}\nfunction beforeUpdate(fn) {\n get_current_component().$$.before_update.push(fn);\n}\nfunction onMount(fn) {\n get_current_component().$$.on_mount.push(fn);\n}\nfunction afterUpdate(fn) {\n get_current_component().$$.after_update.push(fn);\n}\nfunction onDestroy(fn) {\n get_current_component().$$.on_destroy.push(fn);\n}\nfunction createEventDispatcher() {\n const component = get_current_component();\n return (type, detail) => {\n const callbacks = component.$$.callbacks[type];\n if (callbacks) {\n // TODO are there situations where events could be dispatched\n // in a server (non-DOM) environment?\n const event = custom_event(type, detail);\n callbacks.slice().forEach(fn => {\n fn.call(component, event);\n });\n }\n };\n}\nfunction setContext(key, context) {\n get_current_component().$$.context.set(key, context);\n}\nfunction getContext(key) {\n return get_current_component().$$.context.get(key);\n}\nfunction getAllContexts() {\n return get_current_component().$$.context;\n}\nfunction hasContext(key) {\n return get_current_component().$$.context.has(key);\n}\n// TODO figure out if we still want to support\n// shorthand events, or if we want to implement\n// a real bubbling mechanism\nfunction bubble(component, event) {\n const callbacks = component.$$.callbacks[event.type];\n if (callbacks) {\n // @ts-ignore\n callbacks.slice().forEach(fn => fn.call(this, event));\n }\n}\n\nconst dirty_components = [];\nconst intros = { enabled: false };\nconst binding_callbacks = [];\nconst render_callbacks = [];\nconst flush_callbacks = [];\nconst resolved_promise = Promise.resolve();\nlet update_scheduled = false;\nfunction schedule_update() {\n if (!update_scheduled) {\n update_scheduled = true;\n resolved_promise.then(flush);\n }\n}\nfunction tick() {\n schedule_update();\n return resolved_promise;\n}\nfunction add_render_callback(fn) {\n render_callbacks.push(fn);\n}\nfunction add_flush_callback(fn) {\n flush_callbacks.push(fn);\n}\nlet flushing = false;\nconst seen_callbacks = new Set();\nfunction flush() {\n if (flushing)\n return;\n flushing = true;\n do {\n // first, call beforeUpdate functions\n // and update components\n for (let i = 0; i < dirty_components.length; i += 1) {\n const component = dirty_components[i];\n set_current_component(component);\n update(component.$$);\n }\n set_current_component(null);\n dirty_components.length = 0;\n while (binding_callbacks.length)\n binding_callbacks.pop()();\n // then, once components are updated, call\n // afterUpdate functions. This may cause\n // subsequent updates...\n for (let i = 0; i < render_callbacks.length; i += 1) {\n const callback = render_callbacks[i];\n if (!seen_callbacks.has(callback)) {\n // ...so guard against infinite loops\n seen_callbacks.add(callback);\n callback();\n }\n }\n render_callbacks.length = 0;\n } while (dirty_components.length);\n while (flush_callbacks.length) {\n flush_callbacks.pop()();\n }\n update_scheduled = false;\n flushing = false;\n seen_callbacks.clear();\n}\nfunction update($$) {\n if ($$.fragment !== null) {\n $$.update();\n run_all($$.before_update);\n const dirty = $$.dirty;\n $$.dirty = [-1];\n $$.fragment && $$.fragment.p($$.ctx, dirty);\n $$.after_update.forEach(add_render_callback);\n }\n}\n\nlet promise;\nfunction wait() {\n if (!promise) {\n promise = Promise.resolve();\n promise.then(() => {\n promise = null;\n });\n }\n return promise;\n}\nfunction dispatch(node, direction, kind) {\n node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`));\n}\nconst outroing = new Set();\nlet outros;\nfunction group_outros() {\n outros = {\n r: 0,\n c: [],\n p: outros // parent group\n };\n}\nfunction check_outros() {\n if (!outros.r) {\n run_all(outros.c);\n }\n outros = outros.p;\n}\nfunction transition_in(block, local) {\n if (block && block.i) {\n outroing.delete(block);\n block.i(local);\n }\n}\nfunction transition_out(block, local, detach, callback) {\n if (block && block.o) {\n if (outroing.has(block))\n return;\n outroing.add(block);\n outros.c.push(() => {\n outroing.delete(block);\n if (callback) {\n if (detach)\n block.d(1);\n callback();\n }\n });\n block.o(local);\n }\n}\nconst null_transition = { duration: 0 };\nfunction create_in_transition(node, fn, params) {\n let config = fn(node, params);\n let running = false;\n let animation_name;\n let task;\n let uid = 0;\n function cleanup() {\n if (animation_name)\n delete_rule(node, animation_name);\n }\n function go() {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n if (css)\n animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);\n tick(0, 1);\n const start_time = now() + delay;\n const end_time = start_time + duration;\n if (task)\n task.abort();\n running = true;\n add_render_callback(() => dispatch(node, true, 'start'));\n task = loop(now => {\n if (running) {\n if (now >= end_time) {\n tick(1, 0);\n dispatch(node, true, 'end');\n cleanup();\n return running = false;\n }\n if (now >= start_time) {\n const t = easing((now - start_time) / duration);\n tick(t, 1 - t);\n }\n }\n return running;\n });\n }\n let started = false;\n return {\n start() {\n if (started)\n return;\n started = true;\n delete_rule(node);\n if (is_function(config)) {\n config = config();\n wait().then(go);\n }\n else {\n go();\n }\n },\n invalidate() {\n started = false;\n },\n end() {\n if (running) {\n cleanup();\n running = false;\n }\n }\n };\n}\nfunction create_out_transition(node, fn, params) {\n let config = fn(node, params);\n let running = true;\n let animation_name;\n const group = outros;\n group.r += 1;\n function go() {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n if (css)\n animation_name = create_rule(node, 1, 0, duration, delay, easing, css);\n const start_time = now() + delay;\n const end_time = start_time + duration;\n add_render_callback(() => dispatch(node, false, 'start'));\n loop(now => {\n if (running) {\n if (now >= end_time) {\n tick(0, 1);\n dispatch(node, false, 'end');\n if (!--group.r) {\n // this will result in `end()` being called,\n // so we don't need to clean up here\n run_all(group.c);\n }\n return false;\n }\n if (now >= start_time) {\n const t = easing((now - start_time) / duration);\n tick(1 - t, t);\n }\n }\n return running;\n });\n }\n if (is_function(config)) {\n wait().then(() => {\n // @ts-ignore\n config = config();\n go();\n });\n }\n else {\n go();\n }\n return {\n end(reset) {\n if (reset && config.tick) {\n config.tick(1, 0);\n }\n if (running) {\n if (animation_name)\n delete_rule(node, animation_name);\n running = false;\n }\n }\n };\n}\nfunction create_bidirectional_transition(node, fn, params, intro) {\n let config = fn(node, params);\n let t = intro ? 0 : 1;\n let running_program = null;\n let pending_program = null;\n let animation_name = null;\n function clear_animation() {\n if (animation_name)\n delete_rule(node, animation_name);\n }\n function init(program, duration) {\n const d = (program.b - t);\n duration *= Math.abs(d);\n return {\n a: t,\n b: program.b,\n d,\n duration,\n start: program.start,\n end: program.start + duration,\n group: program.group\n };\n }\n function go(b) {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n const program = {\n start: now() + delay,\n b\n };\n if (!b) {\n // @ts-ignore todo: improve typings\n program.group = outros;\n outros.r += 1;\n }\n if (running_program || pending_program) {\n pending_program = program;\n }\n else {\n // if this is an intro, and there's a delay, we need to do\n // an initial tick and/or apply CSS animation immediately\n if (css) {\n clear_animation();\n animation_name = create_rule(node, t, b, duration, delay, easing, css);\n }\n if (b)\n tick(0, 1);\n running_program = init(program, duration);\n add_render_callback(() => dispatch(node, b, 'start'));\n loop(now => {\n if (pending_program && now > pending_program.start) {\n running_program = init(pending_program, duration);\n pending_program = null;\n dispatch(node, running_program.b, 'start');\n if (css) {\n clear_animation();\n animation_name = create_rule(node, t, running_program.b, running_program.duration, 0, easing, config.css);\n }\n }\n if (running_program) {\n if (now >= running_program.end) {\n tick(t = running_program.b, 1 - t);\n dispatch(node, running_program.b, 'end');\n if (!pending_program) {\n // we're done\n if (running_program.b) {\n // intro — we can tidy up immediately\n clear_animation();\n }\n else {\n // outro — needs to be coordinated\n if (!--running_program.group.r)\n run_all(running_program.group.c);\n }\n }\n running_program = null;\n }\n else if (now >= running_program.start) {\n const p = now - running_program.start;\n t = running_program.a + running_program.d * easing(p / running_program.duration);\n tick(t, 1 - t);\n }\n }\n return !!(running_program || pending_program);\n });\n }\n }\n return {\n run(b) {\n if (is_function(config)) {\n wait().then(() => {\n // @ts-ignore\n config = config();\n go(b);\n });\n }\n else {\n go(b);\n }\n },\n end() {\n clear_animation();\n running_program = pending_program = null;\n }\n };\n}\n\nfunction handle_promise(promise, info) {\n const token = info.token = {};\n function update(type, index, key, value) {\n if (info.token !== token)\n return;\n info.resolved = value;\n let child_ctx = info.ctx;\n if (key !== undefined) {\n child_ctx = child_ctx.slice();\n child_ctx[key] = value;\n }\n const block = type && (info.current = type)(child_ctx);\n let needs_flush = false;\n if (info.block) {\n if (info.blocks) {\n info.blocks.forEach((block, i) => {\n if (i !== index && block) {\n group_outros();\n transition_out(block, 1, 1, () => {\n if (info.blocks[i] === block) {\n info.blocks[i] = null;\n }\n });\n check_outros();\n }\n });\n }\n else {\n info.block.d(1);\n }\n block.c();\n transition_in(block, 1);\n block.m(info.mount(), info.anchor);\n needs_flush = true;\n }\n info.block = block;\n if (info.blocks)\n info.blocks[index] = block;\n if (needs_flush) {\n flush();\n }\n }\n if (is_promise(promise)) {\n const current_component = get_current_component();\n promise.then(value => {\n set_current_component(current_component);\n update(info.then, 1, info.value, value);\n set_current_component(null);\n }, error => {\n set_current_component(current_component);\n update(info.catch, 2, info.error, error);\n set_current_component(null);\n if (!info.hasCatch) {\n throw error;\n }\n });\n // if we previously had a then/catch block, destroy it\n if (info.current !== info.pending) {\n update(info.pending, 0);\n return true;\n }\n }\n else {\n if (info.current !== info.then) {\n update(info.then, 1, info.value, promise);\n return true;\n }\n info.resolved = promise;\n }\n}\nfunction update_await_block_branch(info, ctx, dirty) {\n const child_ctx = ctx.slice();\n const { resolved } = info;\n if (info.current === info.then) {\n child_ctx[info.value] = resolved;\n }\n if (info.current === info.catch) {\n child_ctx[info.error] = resolved;\n }\n info.block.p(child_ctx, dirty);\n}\n\nconst globals = (typeof window !== 'undefined'\n ? window\n : typeof globalThis !== 'undefined'\n ? globalThis\n : global);\n\nfunction destroy_block(block, lookup) {\n block.d(1);\n lookup.delete(block.key);\n}\nfunction outro_and_destroy_block(block, lookup) {\n transition_out(block, 1, 1, () => {\n lookup.delete(block.key);\n });\n}\nfunction fix_and_destroy_block(block, lookup) {\n block.f();\n destroy_block(block, lookup);\n}\nfunction fix_and_outro_and_destroy_block(block, lookup) {\n block.f();\n outro_and_destroy_block(block, lookup);\n}\nfunction update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) {\n let o = old_blocks.length;\n let n = list.length;\n let i = o;\n const old_indexes = {};\n while (i--)\n old_indexes[old_blocks[i].key] = i;\n const new_blocks = [];\n const new_lookup = new Map();\n const deltas = new Map();\n i = n;\n while (i--) {\n const child_ctx = get_context(ctx, list, i);\n const key = get_key(child_ctx);\n let block = lookup.get(key);\n if (!block) {\n block = create_each_block(key, child_ctx);\n block.c();\n }\n else if (dynamic) {\n block.p(child_ctx, dirty);\n }\n new_lookup.set(key, new_blocks[i] = block);\n if (key in old_indexes)\n deltas.set(key, Math.abs(i - old_indexes[key]));\n }\n const will_move = new Set();\n const did_move = new Set();\n function insert(block) {\n transition_in(block, 1);\n block.m(node, next);\n lookup.set(block.key, block);\n next = block.first;\n n--;\n }\n while (o && n) {\n const new_block = new_blocks[n - 1];\n const old_block = old_blocks[o - 1];\n const new_key = new_block.key;\n const old_key = old_block.key;\n if (new_block === old_block) {\n // do nothing\n next = new_block.first;\n o--;\n n--;\n }\n else if (!new_lookup.has(old_key)) {\n // remove old block\n destroy(old_block, lookup);\n o--;\n }\n else if (!lookup.has(new_key) || will_move.has(new_key)) {\n insert(new_block);\n }\n else if (did_move.has(old_key)) {\n o--;\n }\n else if (deltas.get(new_key) > deltas.get(old_key)) {\n did_move.add(new_key);\n insert(new_block);\n }\n else {\n will_move.add(old_key);\n o--;\n }\n }\n while (o--) {\n const old_block = old_blocks[o];\n if (!new_lookup.has(old_block.key))\n destroy(old_block, lookup);\n }\n while (n)\n insert(new_blocks[n - 1]);\n return new_blocks;\n}\nfunction validate_each_keys(ctx, list, get_context, get_key) {\n const keys = new Set();\n for (let i = 0; i < list.length; i++) {\n const key = get_key(get_context(ctx, list, i));\n if (keys.has(key)) {\n throw new Error('Cannot have duplicate keys in a keyed each');\n }\n keys.add(key);\n }\n}\n\nfunction get_spread_update(levels, updates) {\n const update = {};\n const to_null_out = {};\n const accounted_for = { $$scope: 1 };\n let i = levels.length;\n while (i--) {\n const o = levels[i];\n const n = updates[i];\n if (n) {\n for (const key in o) {\n if (!(key in n))\n to_null_out[key] = 1;\n }\n for (const key in n) {\n if (!accounted_for[key]) {\n update[key] = n[key];\n accounted_for[key] = 1;\n }\n }\n levels[i] = n;\n }\n else {\n for (const key in o) {\n accounted_for[key] = 1;\n }\n }\n }\n for (const key in to_null_out) {\n if (!(key in update))\n update[key] = undefined;\n }\n return update;\n}\nfunction get_spread_object(spread_props) {\n return typeof spread_props === 'object' && spread_props !== null ? spread_props : {};\n}\n\n// source: https://html.spec.whatwg.org/multipage/indices.html\nconst boolean_attributes = new Set([\n 'allowfullscreen',\n 'allowpaymentrequest',\n 'async',\n 'autofocus',\n 'autoplay',\n 'checked',\n 'controls',\n 'default',\n 'defer',\n 'disabled',\n 'formnovalidate',\n 'hidden',\n 'ismap',\n 'loop',\n 'multiple',\n 'muted',\n 'nomodule',\n 'novalidate',\n 'open',\n 'playsinline',\n 'readonly',\n 'required',\n 'reversed',\n 'selected'\n]);\n\nconst invalid_attribute_name_character = /[\\s'\">/=\\u{FDD0}-\\u{FDEF}\\u{FFFE}\\u{FFFF}\\u{1FFFE}\\u{1FFFF}\\u{2FFFE}\\u{2FFFF}\\u{3FFFE}\\u{3FFFF}\\u{4FFFE}\\u{4FFFF}\\u{5FFFE}\\u{5FFFF}\\u{6FFFE}\\u{6FFFF}\\u{7FFFE}\\u{7FFFF}\\u{8FFFE}\\u{8FFFF}\\u{9FFFE}\\u{9FFFF}\\u{AFFFE}\\u{AFFFF}\\u{BFFFE}\\u{BFFFF}\\u{CFFFE}\\u{CFFFF}\\u{DFFFE}\\u{DFFFF}\\u{EFFFE}\\u{EFFFF}\\u{FFFFE}\\u{FFFFF}\\u{10FFFE}\\u{10FFFF}]/u;\n// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n// https://infra.spec.whatwg.org/#noncharacter\nfunction spread(args, classes_to_add) {\n const attributes = Object.assign({}, ...args);\n if (classes_to_add) {\n if (attributes.class == null) {\n attributes.class = classes_to_add;\n }\n else {\n attributes.class += ' ' + classes_to_add;\n }\n }\n let str = '';\n Object.keys(attributes).forEach(name => {\n if (invalid_attribute_name_character.test(name))\n return;\n const value = attributes[name];\n if (value === true)\n str += ' ' + name;\n else if (boolean_attributes.has(name.toLowerCase())) {\n if (value)\n str += ' ' + name;\n }\n else if (value != null) {\n str += ` ${name}=\"${value}\"`;\n }\n });\n return str;\n}\nconst escaped = {\n '\"': '"',\n \"'\": ''',\n '&': '&',\n '<': '<',\n '>': '>'\n};\nfunction escape(html) {\n return String(html).replace(/[\"'&<>]/g, match => escaped[match]);\n}\nfunction escape_attribute_value(value) {\n return typeof value === 'string' ? escape(value) : value;\n}\nfunction escape_object(obj) {\n const result = {};\n for (const key in obj) {\n result[key] = escape_attribute_value(obj[key]);\n }\n return result;\n}\nfunction each(items, fn) {\n let str = '';\n for (let i = 0; i < items.length; i += 1) {\n str += fn(items[i], i);\n }\n return str;\n}\nconst missing_component = {\n $$render: () => ''\n};\nfunction validate_component(component, name) {\n if (!component || !component.$$render) {\n if (name === 'svelte:component')\n name += ' this={...}';\n throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`);\n }\n return component;\n}\nfunction debug(file, line, column, values) {\n console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console\n console.log(values); // eslint-disable-line no-console\n return '';\n}\nlet on_destroy;\nfunction create_ssr_component(fn) {\n function $$render(result, props, bindings, slots, context) {\n const parent_component = current_component;\n const $$ = {\n on_destroy,\n context: new Map(parent_component ? parent_component.$$.context : context || []),\n // these will be immediately discarded\n on_mount: [],\n before_update: [],\n after_update: [],\n callbacks: blank_object()\n };\n set_current_component({ $$ });\n const html = fn(result, props, bindings, slots);\n set_current_component(parent_component);\n return html;\n }\n return {\n render: (props = {}, { $$slots = {}, context = new Map() } = {}) => {\n on_destroy = [];\n const result = { title: '', head: '', css: new Set() };\n const html = $$render(result, props, {}, $$slots, context);\n run_all(on_destroy);\n return {\n html,\n css: {\n code: Array.from(result.css).map(css => css.code).join('\\n'),\n map: null // TODO\n },\n head: result.title + result.head\n };\n },\n $$render\n };\n}\nfunction add_attribute(name, value, boolean) {\n if (value == null || (boolean && !value))\n return '';\n return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `\"${value}\"`}`}`;\n}\nfunction add_classes(classes) {\n return classes ? ` class=\"${classes}\"` : '';\n}\n\nfunction bind(component, name, callback) {\n const index = component.$$.props[name];\n if (index !== undefined) {\n component.$$.bound[index] = callback;\n callback(component.$$.ctx[index]);\n }\n}\nfunction create_component(block) {\n block && block.c();\n}\nfunction claim_component(block, parent_nodes) {\n block && block.l(parent_nodes);\n}\nfunction mount_component(component, target, anchor, customElement) {\n const { fragment, on_mount, on_destroy, after_update } = component.$$;\n fragment && fragment.m(target, anchor);\n if (!customElement) {\n // onMount happens before the initial afterUpdate\n add_render_callback(() => {\n const new_on_destroy = on_mount.map(run).filter(is_function);\n if (on_destroy) {\n on_destroy.push(...new_on_destroy);\n }\n else {\n // Edge case - component was destroyed immediately,\n // most likely as a result of a binding initialising\n run_all(new_on_destroy);\n }\n component.$$.on_mount = [];\n });\n }\n after_update.forEach(add_render_callback);\n}\nfunction destroy_component(component, detaching) {\n const $$ = component.$$;\n if ($$.fragment !== null) {\n run_all($$.on_destroy);\n $$.fragment && $$.fragment.d(detaching);\n // TODO null out other refs, including component.$$ (but need to\n // preserve final state?)\n $$.on_destroy = $$.fragment = null;\n $$.ctx = [];\n }\n}\nfunction make_dirty(component, i) {\n if (component.$$.dirty[0] === -1) {\n dirty_components.push(component);\n schedule_update();\n component.$$.dirty.fill(0);\n }\n component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));\n}\nfunction init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {\n const parent_component = current_component;\n set_current_component(component);\n const $$ = component.$$ = {\n fragment: null,\n ctx: null,\n // state\n props,\n update: noop,\n not_equal,\n bound: blank_object(),\n // lifecycle\n on_mount: [],\n on_destroy: [],\n on_disconnect: [],\n before_update: [],\n after_update: [],\n context: new Map(parent_component ? parent_component.$$.context : options.context || []),\n // everything else\n callbacks: blank_object(),\n dirty,\n skip_bound: false,\n root: options.target || parent_component.$$.root\n };\n append_styles && append_styles($$.root);\n let ready = false;\n $$.ctx = instance\n ? instance(component, options.props || {}, (i, ret, ...rest) => {\n const value = rest.length ? rest[0] : ret;\n if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {\n if (!$$.skip_bound && $$.bound[i])\n $$.bound[i](value);\n if (ready)\n make_dirty(component, i);\n }\n return ret;\n })\n : [];\n $$.update();\n ready = true;\n run_all($$.before_update);\n // `false` as a special case of no DOM component\n $$.fragment = create_fragment ? create_fragment($$.ctx) : false;\n if (options.target) {\n if (options.hydrate) {\n start_hydrating();\n const nodes = children(options.target);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n $$.fragment && $$.fragment.l(nodes);\n nodes.forEach(detach);\n }\n else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n $$.fragment && $$.fragment.c();\n }\n if (options.intro)\n transition_in(component.$$.fragment);\n mount_component(component, options.target, options.anchor, options.customElement);\n end_hydrating();\n flush();\n }\n set_current_component(parent_component);\n}\nlet SvelteElement;\nif (typeof HTMLElement === 'function') {\n SvelteElement = class extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n connectedCallback() {\n const { on_mount } = this.$$;\n this.$$.on_disconnect = on_mount.map(run).filter(is_function);\n // @ts-ignore todo: improve typings\n for (const key in this.$$.slotted) {\n // @ts-ignore todo: improve typings\n this.appendChild(this.$$.slotted[key]);\n }\n }\n attributeChangedCallback(attr, _oldValue, newValue) {\n this[attr] = newValue;\n }\n disconnectedCallback() {\n run_all(this.$$.on_disconnect);\n }\n $destroy() {\n destroy_component(this, 1);\n this.$destroy = noop;\n }\n $on(type, callback) {\n // TODO should this delegate to addEventListener?\n const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));\n callbacks.push(callback);\n return () => {\n const index = callbacks.indexOf(callback);\n if (index !== -1)\n callbacks.splice(index, 1);\n };\n }\n $set($$props) {\n if (this.$$set && !is_empty($$props)) {\n this.$$.skip_bound = true;\n this.$$set($$props);\n this.$$.skip_bound = false;\n }\n }\n };\n}\n/**\n * Base class for Svelte components. Used when dev=false.\n */\nclass SvelteComponent {\n $destroy() {\n destroy_component(this, 1);\n this.$destroy = noop;\n }\n $on(type, callback) {\n const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));\n callbacks.push(callback);\n return () => {\n const index = callbacks.indexOf(callback);\n if (index !== -1)\n callbacks.splice(index, 1);\n };\n }\n $set($$props) {\n if (this.$$set && !is_empty($$props)) {\n this.$$.skip_bound = true;\n this.$$set($$props);\n this.$$.skip_bound = false;\n }\n }\n}\n\nfunction dispatch_dev(type, detail) {\n document.dispatchEvent(custom_event(type, Object.assign({ version: '3.42.1' }, detail), true));\n}\nfunction append_dev(target, node) {\n dispatch_dev('SvelteDOMInsert', { target, node });\n append(target, node);\n}\nfunction append_hydration_dev(target, node) {\n dispatch_dev('SvelteDOMInsert', { target, node });\n append_hydration(target, node);\n}\nfunction insert_dev(target, node, anchor) {\n dispatch_dev('SvelteDOMInsert', { target, node, anchor });\n insert(target, node, anchor);\n}\nfunction insert_hydration_dev(target, node, anchor) {\n dispatch_dev('SvelteDOMInsert', { target, node, anchor });\n insert_hydration(target, node, anchor);\n}\nfunction detach_dev(node) {\n dispatch_dev('SvelteDOMRemove', { node });\n detach(node);\n}\nfunction detach_between_dev(before, after) {\n while (before.nextSibling && before.nextSibling !== after) {\n detach_dev(before.nextSibling);\n }\n}\nfunction detach_before_dev(after) {\n while (after.previousSibling) {\n detach_dev(after.previousSibling);\n }\n}\nfunction detach_after_dev(before) {\n while (before.nextSibling) {\n detach_dev(before.nextSibling);\n }\n}\nfunction listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {\n const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];\n if (has_prevent_default)\n modifiers.push('preventDefault');\n if (has_stop_propagation)\n modifiers.push('stopPropagation');\n dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });\n const dispose = listen(node, event, handler, options);\n return () => {\n dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });\n dispose();\n };\n}\nfunction attr_dev(node, attribute, value) {\n attr(node, attribute, value);\n if (value == null)\n dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });\n else\n dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });\n}\nfunction prop_dev(node, property, value) {\n node[property] = value;\n dispatch_dev('SvelteDOMSetProperty', { node, property, value });\n}\nfunction dataset_dev(node, property, value) {\n node.dataset[property] = value;\n dispatch_dev('SvelteDOMSetDataset', { node, property, value });\n}\nfunction set_data_dev(text, data) {\n data = '' + data;\n if (text.wholeText === data)\n return;\n dispatch_dev('SvelteDOMSetData', { node: text, data });\n text.data = data;\n}\nfunction validate_each_argument(arg) {\n if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {\n let msg = '{#each} only iterates over array-like objects.';\n if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {\n msg += ' You can use a spread to convert this iterable into an array.';\n }\n throw new Error(msg);\n }\n}\nfunction validate_slots(name, slot, keys) {\n for (const slot_key of Object.keys(slot)) {\n if (!~keys.indexOf(slot_key)) {\n console.warn(`<${name}> received an unexpected slot \"${slot_key}\".`);\n }\n }\n}\n/**\n * Base class for Svelte components with some minor dev-enhancements. Used when dev=true.\n */\nclass SvelteComponentDev extends SvelteComponent {\n constructor(options) {\n if (!options || (!options.target && !options.$$inline)) {\n throw new Error(\"'target' is a required option\");\n }\n super();\n }\n $destroy() {\n super.$destroy();\n this.$destroy = () => {\n console.warn('Component was already destroyed'); // eslint-disable-line no-console\n };\n }\n $capture_state() { }\n $inject_state() { }\n}\n/**\n * Base class to create strongly typed Svelte components.\n * This only exists for typing purposes and should be used in `.d.ts` files.\n *\n * ### Example:\n *\n * You have component library on npm called `component-library`, from which\n * you export a component called `MyComponent`. For Svelte+TypeScript users,\n * you want to provide typings. Therefore you create a `index.d.ts`:\n * ```ts\n * import { SvelteComponentTyped } from \"svelte\";\n * export class MyComponent extends SvelteComponentTyped<{foo: string}> {}\n * ```\n * Typing this makes it possible for IDEs like VS Code with the Svelte extension\n * to provide intellisense and to use the component like this in a Svelte file\n * with TypeScript:\n * ```svelte\n * \n * \n * ```\n *\n * #### Why not make this part of `SvelteComponent(Dev)`?\n * Because\n * ```ts\n * class ASubclassOfSvelteComponent extends SvelteComponent<{foo: string}> {}\n * const component: typeof SvelteComponent = ASubclassOfSvelteComponent;\n * ```\n * will throw a type error, so we need to separate the more strictly typed class.\n */\nclass SvelteComponentTyped extends SvelteComponentDev {\n constructor(options) {\n super(options);\n }\n}\nfunction loop_guard(timeout) {\n const start = Date.now();\n return () => {\n if (Date.now() - start > timeout) {\n throw new Error('Infinite loop detected');\n }\n };\n}\n\nexport { HtmlTag, HtmlTagHydration, SvelteComponent, SvelteComponentDev, SvelteComponentTyped, SvelteElement, action_destroyer, add_attribute, add_classes, add_flush_callback, add_location, add_render_callback, add_resize_listener, add_transform, afterUpdate, append, append_dev, append_empty_stylesheet, append_hydration, append_hydration_dev, append_styles, assign, attr, attr_dev, attribute_to_object, beforeUpdate, bind, binding_callbacks, blank_object, bubble, check_outros, children, claim_component, claim_element, claim_html_tag, claim_space, claim_text, clear_loops, component_subscribe, compute_rest_props, compute_slots, createEventDispatcher, create_animation, create_bidirectional_transition, create_component, create_in_transition, create_out_transition, create_slot, create_ssr_component, current_component, custom_event, dataset_dev, debug, destroy_block, destroy_component, destroy_each, detach, detach_after_dev, detach_before_dev, detach_between_dev, detach_dev, dirty_components, dispatch_dev, each, element, element_is, empty, end_hydrating, escape, escape_attribute_value, escape_object, escaped, exclude_internal_props, fix_and_destroy_block, fix_and_outro_and_destroy_block, fix_position, flush, getAllContexts, getContext, get_all_dirty_from_scope, get_binding_group_value, get_current_component, get_custom_elements_slots, get_root_for_style, get_slot_changes, get_spread_object, get_spread_update, get_store_value, globals, group_outros, handle_promise, hasContext, has_prop, identity, init, insert, insert_dev, insert_hydration, insert_hydration_dev, intros, invalid_attribute_name_character, is_client, is_crossorigin, is_empty, is_function, is_promise, listen, listen_dev, loop, loop_guard, missing_component, mount_component, noop, not_equal, now, null_to_empty, object_without_properties, onDestroy, onMount, once, outro_and_destroy_block, prevent_default, prop_dev, query_selector_all, raf, run, run_all, safe_not_equal, schedule_update, select_multiple_value, select_option, select_options, select_value, self, setContext, set_attributes, set_current_component, set_custom_element_data, set_data, set_data_dev, set_input_type, set_input_value, set_now, set_raf, set_store_value, set_style, set_svg_attributes, space, spread, src_url_equal, start_hydrating, stop_propagation, subscribe, svg_element, text, tick, time_ranges_to_array, to_number, toggle_class, transition_in, transition_out, trusted, update_await_block_branch, update_keyed_each, update_slot, update_slot_base, validate_component, validate_each_argument, validate_each_keys, validate_slots, validate_store, xlink_attr };\n","(function(a,b){if(\"function\"==typeof define&&define.amd)define([],b);else if(\"undefined\"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){\"use strict\";function b(a,b){return\"undefined\"==typeof b?b={autoBom:!1}:\"object\"!=typeof b&&(console.warn(\"Deprecated: Expected third argument to be a object\"),b={autoBom:!b}),b.autoBom&&/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(a.type)?new Blob([\"\\uFEFF\",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",a),d.responseType=\"blob\",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error(\"could not download file\")},d.send()}function d(a){var b=new XMLHttpRequest;b.open(\"HEAD\",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent(\"click\"))}catch(c){var b=document.createEvent(\"MouseEvents\");b.initMouseEvent(\"click\",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f=\"object\"==typeof window&&window.window===window?window:\"object\"==typeof self&&self.self===self?self:\"object\"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||(\"object\"!=typeof window||window!==f?function(){}:\"download\"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement(\"a\");g=g||b.name||\"download\",j.download=g,j.rel=\"noopener\",\"string\"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target=\"_blank\")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:\"msSaveOrOpenBlob\"in navigator?function(f,g,h){if(g=g||f.name||\"download\",\"string\"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement(\"a\");i.href=f,i.target=\"_blank\",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open(\"\",\"_blank\"),g&&(g.document.title=g.document.body.innerText=\"downloading...\"),\"string\"==typeof b)return c(b,d,e);var h=\"application/octet-stream\"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\\/[\\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&\"undefined\"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,\"data:attachment/file;\"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,\"undefined\"!=typeof module&&(module.exports=g)});\n\n//# sourceMappingURL=FileSaver.min.js.map","import { MIDI } from 'src/controller/audio'\nimport { looking } from 'src/controller/controls'\nimport { options } from 'src/input/browser'\nimport { key_down } from 'src/input/keyboard'\nimport { mouse_wheel } from 'src/input/mouse'\nimport { Value } from 'src/value'\nimport { Vector2 } from 'three'\nimport { EVar } from './weave'\n\nexport const landing_shown = new Value(window.location.pathname === '/')\n\nexport const timeline_shown = new Value(options.$.has('DEV'))\nexport const mirror_shown = new Value(true)\n\nexport const modal_location = new Value(new Vector2())\nexport const modal_options = new Value([])\nexport const modal_default = new Value()\nexport const modal_visible = new Value void)>(false)\n\nexport const modal_cursor = new Value(0)\n\nlet last = false\n\nmodal_visible.on((v) => {\n if (v) {\n last = true\n return\n }\n if (v === last) return\n last = false\n MIDI(80, 100, 0.5)\n})\nkey_down.on((k) => {\n switch (k) {\n case 'Escape':\n landing_shown.set(!landing_shown.$)\n if (looking.$ && landing_shown.$) {\n looking.set(false)\n }\n break\n case '`':\n case '~':\n timeline_shown.set(!timeline_shown.$)\n }\n})\n\nmouse_wheel.on(() => {\n modal_visible.set(false)\n})\n","export function Notify(title: string, body: string) {\n new Notification(title, { body })\n}\n","import fs from 'file-saver'\nimport { set } from 'idb-keyval'\nimport { audio_buffer, audio_name } from 'src/controller/audio'\nimport { loading } from 'src/controller/controls'\nimport { timeline_shown } from 'src/fate/editor'\nimport { Notify } from 'src/notify'\nimport { first } from 'src/realm'\nimport { steam } from 'src/steam'\nimport { dbLoaded, HEADER_START, SIGNATURE } from './load'\n\nexport function SaveScript() {\n if (!dbLoaded) return\n const name = first.$.fate.$.text(0)\n Notify(`Keeping ${name}.lisp`, `Another twist in the Weave.`)\n\n const blob = new Blob([first.$.fate.$.toScript()], {\n type: 'application/lisp',\n })\n fs.saveAs(blob, name + '.lisp')\n}\n\nexport function Screenshot() {\n const canvas = document.getElementById('three')\n // @ts-ignore\n canvas.toBlob(\n function (blob) {\n fs.saveAs(blob, `theiaology-${first.$.fate.$.text(0)}.jpg`)\n },\n 'image/jpeg',\n 0.98\n )\n}\nexport function Publish(name: string, selling: 'sell' | 'share') {\n loading.set(true)\n Notify(`Publishing ${name} to Steam Workshop`, `This may take a bit!`)\n // save screenshot\n const canvas = document.getElementById('three')\n\n // @ts-ignore\n canvas.toBlob(\n function (blob) {\n fs.saveAs(blob, `${name}.jpg`)\n },\n 'image/jpeg',\n 0.75\n )\n\n Save(true, `${name}`)\n\n setTimeout(() => {\n // open publish\n steam.$.post(['publish', name, selling].join('|'))\n }, 3000)\n}\n\n// Save .fate file\nexport function Save(withFile = true, id?: string) {\n const name = first.$.fate.$.text(0)\n const buff = BuildBuffer()\n\n if (withFile) {\n Notify(\n `Keeping ${name}.fate`,\n `Drag n' Drop or Load! Share on Workshop and GitHub.`\n )\n fs.saveAs(\n new Blob([buff], {\n type: 'application/octet-stream',\n }),\n `${id === undefined ? name : id}.fate`\n )\n }\n\n // also save to DB\n set(window.location.pathname, buff)\n}\n\nwindow.addEventListener('keydown', (e) => {\n if (e.key === 's' && e.ctrlKey) {\n Save()\n e.preventDefault()\n }\n})\n\nexport function BuildBuffer() {\n const { fate: timeline } = first.$\n // [THEA, TIME_SIZE, VOX_SIZE, MUSIC_SIZE, 0, 0, ...\n const header = HEADER_START * 2\n const timelineLength = timeline.$.length * 4\n const audioLength = audio_buffer.$ ? audio_buffer.$.byteLength + 12 : 0\n\n let voxLength = 0\n\n Object.entries(first.$.voxes.$).forEach(([key, value]) => {\n // size, string, bytes\n voxLength += 16 + value.view.byteLength\n })\n\n const buffer = new DataView(\n new ArrayBuffer(audioLength + timelineLength + header + voxLength)\n )\n\n SIGNATURE.split('').forEach((s, i) => {\n buffer.setUint8(i, s.charCodeAt(0))\n })\n\n // API version\n buffer.setInt32(HEADER_START + 4 * 3, 0)\n\n // Theiaology\n buffer.setInt32(HEADER_START, timeline.$.length * 4)\n\n for (let i = 0; i < timeline.$.length - 1; i++) {\n buffer.setInt32(i * 4 + header, timeline.$.load(i))\n }\n\n // Music\n\n if (audio_buffer.$ !== undefined) {\n // we store in int length\n buffer.setInt32(HEADER_START + 4, audioLength)\n const audioView =\n audio_buffer.$ instanceof DataView\n ? audio_buffer.$\n : new DataView(audio_buffer.$)\n\n // set audio name\n for (let i = 0; i < Math.min(audio_name.$.length, 12); i++) {\n buffer.setUint8(i + header + timelineLength, audio_name.$.charCodeAt(i))\n }\n\n for (let i = 0; i < audioView.byteLength; i++) {\n buffer.setUint8(i + header + timelineLength + 12, audioView.getUint8(i))\n }\n } else {\n buffer.setInt32(HEADER_START + 4, 0)\n }\n\n // VOX files total int size\n const voxStart = header + timelineLength + audioLength\n buffer.setInt32(HEADER_START + 4 + 4, voxLength)\n\n let cursor = voxStart\n Object.entries(first.$.voxes.$).forEach(([key, value]) => {\n buffer.setInt32(cursor, value.view.byteLength)\n cursor += 4\n\n for (let i = 0; i < Math.min(12, key.length); i++) {\n const v = key.charCodeAt(i)\n buffer.setUint8(cursor + i, Number.isNaN(v) ? 0 : v)\n }\n cursor += 12\n\n for (let i = 0; i < value.view.byteLength; i++) {\n buffer.setUint8(cursor + i, value.view.getUint8(i))\n }\n cursor += value.view.byteLength\n })\n\n return buffer.buffer\n}\n\nsetInterval(() => {\n if (!timeline_shown.$ || !dbLoaded.$) return\n Save(false)\n}, 1000)\n","import { key_down, key_map } from 'src/input/keyboard'\nimport { Value } from 'src/value'\nimport { timeline_shown } from './editor'\n\nexport let navs = {}\nexport const doclick = new Value(false)\n\nexport const cursor = new Value({\n right: 'workspace',\n down: 'music|vox|root',\n tag: 'theiaology',\n})\n\nexport function AttemptNav(dir: 'up' | 'down' | 'left' | 'right') {\n if (!cursor.$[dir]) return\n const attempts = cursor.$[dir].split('|')\n for (let attempt of attempts) {\n if (!navs[attempt]) continue\n cursor.set(navs[attempt])\n return\n }\n}\n// @ts-ignore\nwindow.navs = navs\nkey_down.on((k) => {\n if (!timeline_shown.$) return\n\n switch (k) {\n case 'Enter':\n case 'i':\n doclick.set(true)\n break\n case 'r':\n case 'ArrowUp':\n case 'h':\n AttemptNav('up')\n break\n case 'f':\n case 'ArrowDown':\n case 'j':\n AttemptNav('down')\n break\n case 'q':\n case 'ArrowLeft':\n case 'k':\n AttemptNav('left')\n break\n case 'ArrowRight':\n case 'l':\n AttemptNav('right')\n break\n case 'Tab':\n AttemptNav(key_map.$['Shift'] ? 'left' : 'right')\n break\n }\n})\n\nexport interface INav {\n left?: string\n up?: string\n right?: string\n down?: string\n tag: string\n i?: number\n}\n","\r\n\r\n\r\n\r\n {\r\n e.preventDefault()\r\n if (nav.tag !== '') cursor.set(nav)\r\n }}\r\n bind:this={box}\r\n on:click={doClick}\r\n class:notilt\r\n on:focus={() => {}}\r\n on:mouseover={() => {\r\n mouseOver()\r\n if (hover === '') return\r\n\r\n modal_location.set(vec2.copy(mouse_page.$).add(offset))\r\n modal_visible.set(() => {})\r\n modal_options.set(hover)\r\n \r\n }}\r\n style=\"filter: hue-rotate({selected ? 90 : tilt}deg);{style}\"\r\n>\r\n \r\n\r\n\r\n\r\n","import { Color } from 'three'\n\nexport const stringToColor = (str) => hashcode(str) % 0xffffff\nexport const hashcode = (str) => {\n str = str || ' '\n let hash = 0,\n i,\n chr\n\n for (i = 0; i < str.length; i++) {\n chr = str.charCodeAt(i)\n hash = (hash << 5) - hash + chr\n hash |= 0\n }\n\n return Math.abs(hash)\n}\n\nconst $col = new Color()\n\nexport const faintColor = (str) => {\n return `rgba(${$col.set(stringToColor(str)).r}, ${$col.g}, ${$col.b}, 0.25)`\n}\n","\n\n
\n\n\n","\n\n\n\n\n
\n remove(i)}\n nav={{\n i,\n tag: `${order}-remove`,\n right: `${order}-command`,\n up: `${order - 1}-remove|${\n order - 1\n }-command|vox-del-last|music-del|theiaology`,\n down: `${order + 1}-remove|${order + 1}-command`,\n }}\n >\n {i === 0 ? '>' : 'x'}\n \n \n {label}\n \n\n {#if Invocations[invoke]}\n {#each Object.entries(Invocations[invoke]) as [key, value], index (key)}\n {#if value === EVar.STRING}\n \n \"{$fate.text(i)}\"\n \n {:else if value === EVar.SHORTSTRING}\n inputShort(index)}\n nav={NavData(index)}\n >\n \"{IntToString($fate[`data${index}`](i))}\"\n \n {:else if value === EVar.NUMBER || value === EVar.POSITIVE || value == EVar.NEGATIVE}\n inputNumber(index)}\n nav={NavData(index)}\n >\n {$fate[`data${index}`](i)}\n \n {:else if value === EVar.FAENUMBER || value === EVar.FAEPOSITIVE}\n inputFaeNumber(index)}\n nav={NavData(index)}\n >\n {FaeUnits($fate[`data${index}`](i))}\n \n {:else if value == EVar.VOX}\n \n {$fate.text(i) === '' ? 'None' : $fate.text(i)}\n \n {:else if value === EVar.COLOR}\n \n {\n // @ts-ignore\n submitColor(index, parseInt(e.target.value.slice(1), 16))\n }}\n />\n \n {:else if typeof value === 'object'}\n inputEnum(index, value)}\n nav={NavData(index)}\n >\n {value[$fate[`data${index}`](i)]}\n \n {:else if value === EVar.NORMAL}\n inputNormal(index)}\n >\n {Math.abs(($fate[`data${index}`](i) / NORMALIZER) * 100).toFixed(\n 0\n )}%\n \n {:else if value === EVar.BOOL}\n {\n // @ts-ignore\n $fate[`data${index}`](i, $fate[`data${index}`](i) ? 0 : 1)\n fate.poke()\n }}\n >\n \n \n {:else if value === EVar.TIME}\n inputDataTime(index)}\n >\n {d0($fate[`data${index}`](i) / 60)}:{d0(\n $fate[`data${index}`](i) % 60\n )}\n \n {:else if value === EVar.NOISE}\n inputNoise(index)}\n >\n 🎰 {viewer.setInt32(0, $fate[`data${index}`](i)) ||\n viewer.getUint8(0).toString(16)}\n {$fate && viewer.getUint8(1).toString(16)}\n {$fate && viewer.getUint8(2).toString(16)}\n {$fate && viewer.getUint8(3).toString(16)}\n \n {:else if value === EVar.PATTERN}\n inputPattern(index)}\n >\n \n \n {:else}\n \n {/if}\n {/each}\n {:else if i === 0}\n \n \"{$fate.text(i)}\"\n \n {/if}\n\n {#if i === 0 || item.$[1] === ESpell.TOME}\n addTo(i)}\n hover=\"Add\"\n style=\" border-radius: 0 0.5rem 0.5rem 0rem;\"\n nav={{\n i,\n tag: `${order}-add`,\n left: `${order}-data-2|${order}-data-1|${order}-data-0|${order}-command`,\n\n up: `${order - 1}-add|${up}`,\n down: `${order + 1}-add|${order + 1}-data-2|${order + 1}-data-1|${\n order + 1\n }-data-0`,\n }}>+\n {:else if item.$[1] !== ESpell.TOME && item.$[1] !== undefined}\n \n {d0(item.$[0] / 60)}:{d0(item.$[0] % 60)}\n \n {/if}\n
\n
\n {#each myChildren as key}\n \n {/each}\n
\n\n\n{#if i === 0 && $fate}\n {#each rootChildren as key, idx (Math.random())}\n \n {/each}\n{/if}\n\n\n","\n\n
\n
\n\n {#if $audio_buffer}\n
\n {\n audio_buffer.set(undefined)\n audio.src = ''\n audio.load()\n }}>-\n {\n fs.saveAs(\n new Blob([audio_buffer.$], { type: 'audio/mp3' }),\n audio_name.$\n )\n // download audio file\n }}>MUSIC\n {$audio_name}\n
\n {/if}\n {#each Object.keys($voxes) as key, i}\n
\n {\n delete $voxes[key]\n voxes.poke()\n }}>-\n {\n // download vox file\n fs.saveAs(\n new Blob([$voxes[key].view], { type: 'vox' }),\n `${key}.vox`\n )\n }}\n \n hover=\"Download VOX Model File\">VOX\n {key}\n
\n {/each}\n\n\n \n\n
\n
\n\n\n","\n\n\n\n\n","\n\n\n\n\n","\n\n {\n modal_visible.$(e.target.value)\n }}\n/>\n\n\n\n","\n\n {\n modal_visible.$(e.target.value)\n }}\n/>\n\n\n\n\n","\n\n\n{#each $pads as padder, ip}\n \n {#each pad as chance, i }\n {\n if(!left) return\n Click(i, ip)()\n }} click={Click(i, ip)}>\n {($pads[ip] & (1 << i)) ? 'O' : 'X'}\n \n {/each}\n\n \n{/each}\n\n {\n left = true\n}} on:mouseup={() => {\n left = false \n}}/>\n\n","\n\n
\n{#each patterns as i (i)} \n {\n modal_visible.$(i) \n }}>\n{/each}\n
\n\n\n\n\n","\n\n{#if $modal_visible}\n \n window.innerHeight / 2\n ? $modal_location.y - (len / 5) * 60\n : $modal_location.y}px\"\n >\n {#if typeof $modal_options === 'string'}\n \n {$modal_options}\n \n {:else if Array.isArray($modal_options)}\n {#each Group($modal_options) as contentRow}\n
\n {#each contentRow as content}\n {\n if (typeof $modal_visible === 'function')\n $modal_visible(content)\n modal_visible.set(false)\n }}\n >\n
\n {content}\n
\n \n {/each}\n
\n {/each}\n {:else if $modal_options === EVar.TIME}\n