TurboBuilder is currently in development, some project files may break due to code changing all the time.

Extension
Events
Control
Sound
Sensing
Literals
Operators
Conversions
Variables
JSON
Blocks
Functions
Debug
everyseconds doinseconds doifthenifthenelseifthenelseswitchcasedefaultbreakstart soundhttps://t.ly/2gHlMalertpromptconfirmtime (ms) since 1970nulltruefalse(0)'string'blank arrayarray of lengthblank object====><andornot+-*÷^* 10/ 10random int fromtojoinjoinletterofto numberto stringmillisecondstomillisecondssettogetset localstoragetoget localstorageisvalid JSON?stringto JSONJSONto stringinsertat end ofgetfrom arraylength of arraycreate blockid:idtext:texttype:blockinputs:function:create inputid:IDtype:stringdefault:getINPUTIDreturnfunctionidinline functionreturncallidcallidlogwarnerrorraw raw EventsControlSoundSensingLiteralsOperatorsConversionsVariablesJSONBlocksFunctionsDebug

Assets

Extra things that will appear under "Extension" in the block list.
These things are not required, so you can leave them empty if you do not need them.

Documentation URL:

Extension Icon:

Extra Icons

Blocks can use their own icons instead of the Extension icon.
Add more images here to use them.

Extension Code

/* This extension was made with Snail IDE - ExtCreate! it is also really cool 😎 https://extcreate.snail-ide.com/ */ (function(Scratch) { const variables = {}; const blocks = []; const menus = []; function doSound(ab, cd, runtime) { const audioEngine = runtime.audioEngine; const fetchAsArrayBufferWithTimeout = (url) => new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); let timeout = setTimeout(() => { xhr.abort(); reject(new Error("Timed out")); }, 5000); xhr.onload = () => { clearTimeout(timeout); if (xhr.status === 200) { resolve(xhr.response); } else { reject(new Error(`HTTP error ${xhr.status} while fetching ${url}`)); } }; xhr.onerror = () => { clearTimeout(timeout); reject(new Error(`Failed to request ${url}`)); }; xhr.responseType = "arraybuffer"; xhr.open("GET", url); xhr.send(); }); const soundPlayerCache = new Map(); const decodeSoundPlayer = async (url) => { const cached = soundPlayerCache.get(url); if (cached) { if (cached.sound) { return cached.sound; } throw cached.error; } try { const arrayBuffer = await fetchAsArrayBufferWithTimeout(url); const soundPlayer = await audioEngine.decodeSoundPlayer({ data: { buffer: arrayBuffer, }, }); soundPlayerCache.set(url, { sound: soundPlayer, error: null, }); return soundPlayer; } catch (e) { soundPlayerCache.set(url, { sound: null, error: e, }); throw e; } }; const playWithAudioEngine = async (url, target) => { const soundBank = target.sprite.soundBank; let soundPlayer; try { const originalSoundPlayer = await decodeSoundPlayer(url); soundPlayer = originalSoundPlayer.take(); } catch (e) { console.warn( "Could not fetch audio; falling back to primitive approach", e ); return false; } soundBank.addSoundPlayer(soundPlayer); await soundBank.playSound(target, soundPlayer.id); delete soundBank.soundPlayers[soundPlayer.id]; soundBank.playerTargets.delete(soundPlayer.id); soundBank.soundEffects.delete(soundPlayer.id); return true; }; const playWithAudioElement = (url, target) => new Promise((resolve, reject) => { const mediaElement = new Audio(url); mediaElement.volume = target.volume / 100; mediaElement.onended = () => { resolve(); }; mediaElement .play() .then(() => { // Wait for onended }) .catch((err) => { reject(err); }); }); const playSound = async (url, target) => { try { if (!(await Scratch.canFetch(url))) { throw new Error(`Permission to fetch ${url} denied`); } const success = await playWithAudioEngine(url, target); if (!success) { return await playWithAudioElement(url, target); } } catch (e) { console.warn(`All attempts to play ${url} failed`, e); } }; playSound(ab, cd) } class Extension { getInfo() { return { "id": "extensionID", "name": "Extension", "color1": "#0088ff", "color2": "#0063ba", "blocks": blocks } } } Scratch.extensions.register(new Extension()); })(Scratch);