Pi.js v2.3 Upgrade

Pi.js v2.3.0 rewrites the sound plugin on a mixed bus graph, introduces the optional sound-advanced plugin, and gives the keyboard, pointer, and gamepad plugins one shared set of input conventions. This release renames the input commands to camelCase without aliases and changes the sound() envelope arguments, so most applications will need code changes before upgrading.

INTRODUCTION

Pi.js v2.3.0 is a feature release for applications upgrading from any Pi.js v2.x version. Unlike v2.1 and v2.2, it is not a drop-in update: the sound, keyboard, pointer, and gamepad plugins are now version 2.0.0, and their renamed commands have no aliases. An old name is not registered, so code that uses it fails at its first call.

Applications upgrading directly from Pi.js v1.2.x should read the v2.0, v2.1, and v2.2 upgrade guides first because the architectural and API changes introduced in those releases still apply.

UPGRADE AT A GLANCE

  1. EXISTING V2.X APPLICATIONS

    • Drawing, text, image, shader, view, and screen code remains compatible
    • Input commands must be renamed to their camelCase forms
    • Sound code that uses envelopes, audio pools, or PLAY articulation needs review
    • Applications that use neither input nor sound commands should behave as they did in v2.2, apart from stricter validation
  2. NEW CAPABILITIES

    • Control individual playing samples with instance IDs, and stream long audio files
    • Mix sound on sfx, music, audio, and master buses through an output limiter
    • Add synthesis, bus effects, presets, instruments, and recording with the sound-advanced plugin
    • Read mouse-wheel input, name gamepad buttons and axes, and rumble gamepads
    • Detect cancelled input with a cancelled field on input data
    • Share an API between plugins with provideService() and getService()
  3. BEHAVIOR TO REVIEW

    • Input commands are camelCase with no aliases: inkey is now inKey, onmouse is onMouse, and ingamepad is inGamepad
    • sound() uses an ADSR envelope, which changes the meaning of its seventh positional argument
    • loadAudio() no longer accepts poolSize
    • Touch handler modes are "down", "move", and "up"
    • Registering the same input handler twice now runs it once
    • set() throws for options it does not recognize
    • $.clearEvents() clears pointer handlers on every screen
    • Loading a standalone plugin that the full build already includes now throws

NEW FEATURES

  1. AUDIO INSTANCES AND STREAMING

    • playAudio() now returns an instance ID for the sample it started
    • pauseAudio(), resumeAudio(), setAudio(), and stopAudio() accept that instance ID
    • setAudio() changes the volume, playback rate, and pan of a playing instance
    • loadAudio() accepts a stream flag that plays the file through a media element instead of decoding it
    • Streaming suits long music tracks; decoded audio suits short, frequently played effects

    Audio instance example:

    const music = $.loadAudio( "media/theme.mp3", "theme", true );
    $.ready( () => {
    	const instance = $.playAudio( { "audioId": music, "loop": true } );
    	$.setAudio( instance, 0.5 );
    	$.pauseAudio( instance );
    	$.resumeAudio( instance );
    } );
  2. SOUND BUSES AND OUTPUT LIMITER

    • Sound is mixed on three buses before the master volume: sfx, music, and audio
    • sound() plays on sfx, play() on music, and playAudio() on audio
    • setBusVolume() sets the volume of one bus, or of master
    • setSoundLimiter() turns the output limiter on or off; it is on by default
    • This makes separate music and effects volume settings a single call each

    Bus volume example:

    $.setBusVolume( "music", 0.4 );
    $.setBusVolume( "sfx", 1 );
  3. EXTENDED PLAY STRINGS

    • MH sets the envelope sustain level and MR sets the release
    • MP sets the stereo pan
    • WN and WP select white and pink noise waveforms
    • @n selects an instrument defined with the sound-advanced plugin
  4. THE SOUND-ADVANCED PLUGIN

    • sound-advanced 1.0.0 is a new plugin
    • It is not part of either bundle; load it separately after them
    • Requires either the full version of pi.js or the lite version with the sound plugin
    • synth() plays a synthesized voice with pulse waves, a filter and filter envelope, vibrato, tremolo, arpeggio, and periodic noise
    • synth() with hold sustains until releaseSound(), and setSynth() changes it while it plays
    • sfx() plays a preset, definePreset() adds one, and generateSfx() builds a repeatable sound effect from a seed
    • defineInstrument() defines a PLAY instrument, synthesized or from a loaded audio file
    • setBusEffect() adds reverb, delay, filter, distortion, bitcrush, or chorus to a bus, alone or as a chain
    • getSoundLevels() reports the levels, spectrum, and waveform of a bus or of the output
    • startRecording(), stopRecording(), getRecordingState(), and saveRecording() record a bus or the output to a WAV file
    • onPlay() and offPlay() run handlers at each note’s audible time, for visuals synced to music

    Bus effect example:

    $.setBusEffect( "music", "reverb", { "time": 2.5, "mix": 0.4 } );
    $.sfx( "zap", 0.3 );
  5. MOUSE-WHEEL INPUT

    • onWheel() and offWheel() register and remove mouse-wheel handlers
    • Handlers receive { x, y, deltaX, deltaY } in CSS pixels
    • While a screen has wheel handlers, the wheel does not scroll the page over its canvas
    • clearEvents( "wheel" ) removes every wheel handler

    Mouse-wheel example:

    let zoom = 1;
    $.onWheel( ( wheelData ) => {
    	zoom = Math.min( Math.max( zoom - wheelData.deltaY / 500, 0.25 ), 4 );
    } );
  6. GAMEPAD NAMES AND VIBRATION

    • Gamepad helpers accept standard-mapping names as well as indices
    • Button names include south, east, west, north, leftShoulder, rightShoulder, leftTrigger, rightTrigger, select, start, leftStick, rightStick, dpadUp, dpadDown, dpadLeft, dpadRight, and home
    • Axis names are leftX, leftY, rightX, and rightY
    • vibrateGamepad() plays a dual-rumble effect for a duration in milliseconds
    • It returns false where the browser or pad cannot vibrate

    Gamepad example:

    const gamepad = $.inGamepad( 0 );
    if( gamepad && gamepad.getButtonJustPressed( "south" ) ) {
    	$.vibrateGamepad( 0, 200, 1, 0.5 );
    }
  7. CANCELLED INPUT

    • Key, mouse, touch, press, and click data have a new cancelled field
    • It is set when a release comes from blur, a hidden page, a stop command, or the browser cancelling the input
    • Handlers can use it to tell a real release from one the user did not make
  8. PLUGIN SERVICES

    • provideService() lets a plugin publish an API during initialization
    • getService() returns the service of a plugin it depends on
    • The sound plugin provides a sound service that sound-advanced uses
    • getPlugins() now reports each plugin’s state

BEHAVIORAL CHANGES

  1. INPUT COMMANDS RENAMED

    • Keyboard: inkey, onkey, and offkey are now inKey, onKey, and offKey
    • Mouse: inmouse, onmouse, and offmouse are now inMouse, onMouse, and offMouse
    • Touch: intouch, ontouch, and offtouch are now inTouch, onTouch, and offTouch
    • Press: inpress, onpress, and offpress are now inPress, onPress, and offPress
    • Click: onclick and offclick are now onClick and offClick
    • Context menu: setEnableContextMenu and the enableContextMenu setting are now setContextMenu and contextMenu
    • Gamepad: ingamepad is now inGamepad
    • Gamepad connections: onGamepadConnected( fn ) and onGamepadDisconnected( fn ) are now onGamepad( "connect", fn ) and onGamepad( "disconnect", fn ), removed with offGamepad()
    • Gamepad dead zone: setGamepadSensitivity and the gamepadSensitivity setting are now setGamepadDeadZone and gamepadDeadZone

    Before v2.3:

    $.onkey( "Enter", "down", start );
    $.onmouse( "down", shoot );
    const pads = $.ingamepad();

    With v2.3:

    $.onKey( "Enter", "down", start );
    $.onMouse( "down", shoot );
    const pads = $.inGamepad();
  2. SOUND USES ADSR ENVELOPES

    • sound() replaces attack and decay with attackTime, decayTime, sustainLevel, and releaseTime
    • The positional order is now frequency, duration, volume, oType, delay, attackTime, decayTime, sustainLevel, releaseTime, pan, and frequencyEnd
    • The seventh argument used to set the tail of the sound; it is now decayTime, and the tail is the 0.1 second default release
    • Frequency is no longer rounded, and the default envelope and a short de-click ramp change the sound slightly
    • A sweep with an endpoint at or below 0 throws INVALID_FREQUENCY

    Before v2.3:

    $.sound( 440, 1, 1, "square", 0, 0.01, 0.5 );

    With v2.3:

    $.sound( {
    	"frequency": 440, "duration": 1, "oType": "square",
    	"attackTime": 0.01, "releaseTime": 0.5
    } );
  3. AUDIO POOLS REMOVED

    • loadAudio() no longer takes poolSize; its third argument is now the stream boolean
    • There is no per-file limit on overlapping playback, only the shared voice limits
    • The stopAudio() object form takes id instead of audioId, and stopping fades out
    • playbackRate is limited to 0.25–4 for streamed audio and 0.0625–16 for decoded audio; values outside the range throw INVALID_PLAYBACK_RATE
    • Default audio IDs are audio_N instead of audioPool_N
    • Audio needs a page served over HTTP or HTTPS; file:// pages throw UNSUPPORTED_PROTOCOL
  4. SHARED VOICE BUDGET AND TIMING

    • Samples, synthesized sounds, and PLAY notes share one voice budget
    • Looping samples count toward it but are never stolen automatically
    • When no voice can be replaced, a new sound is rejected; the call still returns an ID that is already completed
    • Delayed sound() and playAudio() requests are limited to 1024 pending; beyond that the call throws TOO_MANY_PENDING_SOUNDS
    • stopSound(), stopPlay(), and stopAudio() finish about 15 ms after the call with a short fade, instead of at once
    • setVolume() sets one master gain for samples and synthesized sound alike, before the limiter
    • With the limiter on, output above 0.9 is soft-clipped, so mixes that clipped hard now saturate smoothly
  5. PLAY STRING CHANGES

    • The PLAY envelope is ADSR: MA and MD set attack and decay times, MH the sustain level, and MR the release, each as a percentage
    • MT and the undocumented MU, MX, MY, MZ, and MK are ignored with a warning
    • MS, MN, and ML shorten the sounding part of each note and keep the beat, so songs that used MS or MN play at their written tempo, slower than in v2.2
    • A dot after a length is honored: C4. is a dotted quarter
    • C- and B# cross the octave boundary, and N116–N119 play a semitone lower than before
  6. SHARED INPUT CONVENTIONS

    • A handler is identified by its mode and function; registering the same function for the same mode again does nothing
    • State is updated before handlers run, and a handler that throws is reported with console.error while the other handlers still run
    • A handler registered during an event first runs for the next one, and one removed during an event does not run later in it
    • Input data objects and lists are frozen or updated in place; copy a value to keep it
    • Validation throws TypeError or RangeError with a code per parameter instead of INVALID_PARAMETERS
    • Non-boolean flags such as once throw instead of being coerced
  7. KEYBOARD CHANGES

    • The keyboard plugin starts on first use rather than when it loads; call startKeyboard() to track keys from an earlier point
    • offKey() matches on key, mode, and function, and offKey( key ) alone throws
    • Blur, a hidden page, stopKeyboard(), typing into a text field, and an input() prompt release held keys through "up" handlers with cancelled: true
    • Keys typed into an input() prompt, including the Enter that ends it, no longer reach onKey() handlers or inKey()
    • setActionKeys() replaces the action keys instead of adding to them
    • inKey( "" ) and inKey( 0 ) throw instead of returning the list
  8. MOUSE, TOUCH, PRESS, AND CLICK CHANGES

    • Touch handlers use the modes "down", "move", and "up" instead of "start", "move", and "end"; the old modes throw INVALID_MODE
    • Touch handlers run once per touch, with the touch that changed
    • Mouse, touch, press, and click data share one shape: x, y, lastX, lastY, buttons, action, type, id, and cancelled
    • inMouse() and inPress() return null before the first event and after their input stops
    • clearEvents( "press" ) no longer clears click handlers; use "click"
    • setPinchZoom() is now a screen command that sets touch-action on that screen’s canvas and no longer changes <body>
    • A click needs the same pointer’s press and release inside its hit box; right and middle buttons no longer click
    • A drag that leaves the canvas keeps reporting moves and reports its release
    • A hit box that is not an object, or that has a negative size, throws instead of being ignored

    Before v2.3:

    $.ontouch( "start", onTouchStart );
    $.ontouch( "end", onTouchEnd );

    With v2.3:

    $.onTouch( "down", onTouchStart );
    $.onTouch( "up", onTouchEnd );
  9. GAMEPAD CHANGES

    • inGamepad() always returns an array, empty while polling is stopped; inGamepad( i ) returns null for a missing pad
    • Sticks on standard-mapping pads use a radial dead zone, so a full diagonal reads a distance of 1 and near-axis movement no longer snaps to the axis
    • The dead zone must be under 1: setGamepadDeadZone( 1 ) throws
    • “Just pressed”, “just released”, and “axis changed” report what happened since the previous read, so slower loops see every press
    • A connect handler registered later receives the pads already connected
    • A helper index must be an integer or a standard name: getButton( "0" ) now throws
    • stopGamepad() holds even with handlers registered, until startGamepad()
  10. STRICTER CORE VALIDATION

    • set() throws INVALID_OPTION for an option it does not recognize, including options from plugins that are not loaded
    • Every option name is checked first, so a call that throws applies no setting
    • $.clearEvents() clears mouse, touch, press, and click handlers on every screen and cancels an input() prompt on any screen; call clearEvents() on a screen to clear only that screen
    • arc(), loadFont(), loadSpritesheet(), and setPrintSize() reject values they used to accept, such as non-finite angles and negative margins
    • $.removeScreen() throws INVALID_SCREEN_ID for a missing, unknown, or already removed screen
    • An argument passed as undefined counts as omitted and takes the default
    • Loading a standalone plugin that the full build already includes throws DUPLICATE_PLUGIN
    • Registering a plugin after initialization throws REGISTRATION_CLOSED

FIXES AND RESOURCE MANAGEMENT

  1. KEYBOARD AND INPUT PROMPTS

    • A key is released by its code, so a letter pressed with Shift no longer stays held
    • input() reads its own keys, so it works after stopKeyboard()
    • Space no longer scrolls the page and Tab no longer moves focus during a prompt; pasted text is inserted
    • Numeric prompts resolve with a number, and the prompt keeps to one line
    • startKeyboard() no longer blurs the focused element
  2. POINTER HANDLING

    • Removing a handler no longer disables the other handlers of its mode
    • Each touch in inTouch() keeps its own action
    • A release for a button or touch that is not held is ignored
    • A press that starts on the canvas border or padding is ignored, and hit boxes accept fractional values
  3. GAMEPAD RELIABILITY

    • A button press that happens between reads is no longer lost
    • A throwing connection handler no longer stops the others or leaves a pad tracked after it disconnects
  4. DRAWING, FONTS, AND SCREENS

    • setChar() works on every font, including the default font, and leaves the source image passed to loadFont() unchanged
    • getImage() of an offscreen screen returns a canvas copy of its pixels
    • Offscreen screens draw again after a WebGL context loss that happens while no offscreen screen exists
    • blitImage() and blitSprite() accept every color form drawImage() accepts, and their object forms apply the documented defaults
    • polygon() fills shapes with coordinates far off the screen correctly
    • getPal( false ) and getDefaultPal( false ) exclude index 0
    • An image loaded or captured without a name gets one no other image uses
  5. PLUGIN LIFECYCLE AND BUILDS

    • A plugin whose initialization fails leaves nothing installed, and its name can be registered again
    • The circle geometry cache is bounded
    • Builds are identical on every platform

API COMPATIBILITY

Pi.js v2.3.0 renames the input commands of the keyboard, pointer, and gamepad plugins and changes the arguments of several sound commands. No aliases or compatibility shims are provided. Core drawing, text, image, shader, and screen commands are not renamed.

NEW COMMANDS

  • pauseAudio(), resumeAudio(), and setAudio()
  • setBusVolume() and setSoundLimiter()
  • onWheel() and offWheel()
  • onGamepad(), offGamepad(), and vibrateGamepad()

EXTENDED COMMANDS

  • playAudio() returns an instance ID
  • loadAudio() accepts stream and no longer accepts poolSize
  • stopAudio() accepts instance IDs
  • sound() accepts attackTime, decayTime, sustainLevel, and releaseTime in place of attack and decay
  • play() accepts MH, MR, MP, WN, WP, and @n
  • getPlugins() reports each plugin’s state

RENAMED ERROR CODES

  • AUDIO_POOL_NOT_FOUND is now AUDIO_NOT_FOUND
  • EMPTY_POOL is now AUDIO_NOT_LOADED
  • INVALID_POOL_SIZE is removed with poolSize

PLUGINS

  • The sound, keyboard, pointer, and gamepad plugins are now version 2.0.0
  • The sound-advanced plugin is new and is loaded separately with either build
  • The unreleased onscreen-keyboard, pi-vision, print-table, and pens plugins were removed from the source; they were never part of the release package

TYPESCRIPT

  • Projects using nodenext module resolution get the package’s types
  • Lite projects that load a plugin get that plugin’s command, screen command, and setting types
  • The global pi and $ are declared by the full declarations only; lite projects use the module’s exports

 

MIGRATION GUIDE

  1. UPDATE THE LIBRARY

    • Replace all Pi.js v2.x bundle files with matching v2.3 bundle files
    • Keep full, lite, ESM, and IIFE variants consistent with the variant already in use
    • Update separately loaded plugin bundles at the same time when applicable
    • Do not mix v2.2 core files and v2.3-generated type definitions
    • If you use the full build, remove any standalone script for a plugin it already includes
  2. RENAME INPUT COMMANDS

    • Search for the lowercase names: inkey, onkey, offkey, inmouse, onmouse, offmouse, intouch, ontouch, offtouch, inpress, onpress, offpress, onclick, offclick, and ingamepad
    • Replace each with its camelCase form
    • Replace setEnableContextMenu with setContextMenu and setGamepadSensitivity with setGamepadDeadZone, along with their set() option names
    • Replace onGamepadConnected( fn ) and onGamepadDisconnected( fn ) with onGamepad( "connect", fn ) and onGamepad( "disconnect", fn )
    • Take care not to rename DOM properties such as element.onclick that belong to the browser, not Pi.js
  3. UPDATE TOUCH MODES AND INPUT READS

    • Change onTouch( "start" ) to "down" and onTouch( "end" ) to "up"
    • Review touch handlers that expected every touch in one call; they now run once per touch
    • Handle null from inMouse() and inPress() before the first event
    • Replace if( pads ) checks on inGamepad() with a length check, and === undefined checks on a single pad with === null
    • Copy values out of input data objects instead of storing or modifying the objects

    Before v2.3:

    const pads = $.ingamepad();
    if( pads ) {
    	updatePlayers( pads );
    }

    With v2.3:

    const pads = $.inGamepad();
    if( pads.length > 0 ) {
    	updatePlayers( pads );
    }
  4. UPDATE SOUND ENVELOPES

    • Search for sound() calls that pass six or more positional arguments, or the attack and decay options
    • Rewrite them with attackTime, decayTime, sustainLevel, and releaseTime; the object form is the clearest
    • Move a tail length that was the seventh argument to releaseTime
    • Rewrite PLAY envelopes from MAa MTt MDd to MAa MDd MHh MRr with new percentages
    • Listen to songs that use MS or MN; they now play at their written tempo, so raise the tempo if the v2.2 speed was intended
  5. UPDATE AUDIO LOADING AND PLAYBACK

    • Remove poolSize from every loadAudio() call; the third argument is now the stream boolean
    • Change the stopAudio() object form from audioId to id
    • Update error handling that checks AUDIO_POOL_NOT_FOUND, EMPTY_POOL, or INVALID_POOL_SIZE
    • Serve the page over HTTP or HTTPS; audio no longer loads from file:// pages
    • Keep the instance ID from playAudio() when a sample needs to be paused, adjusted, or stopped on its own

    Before v2.3:

    const bomb = $.loadAudio( "media/bomb.wav", "bomb", 4 );

    With v2.3:

    const bomb = $.loadAudio( "media/bomb.wav", "bomb" );
  6. REVIEW SETTINGS AND EVENT CLEANUP

    • Check every set() call for misspelled options or options from plugins that are not loaded
    • Pass every action key in one setActionKeys() call, since it now replaces the list
    • Replace $.clearEvents() with a screen’s clearEvents() where only one screen should be cleared
    • Replace clearEvents( "press" ) with "click" where click handlers should be removed
    • Call setPinchZoom() on a screen, and remove any reliance on it changing <body>
  7. VERIFY EXISTING CODE

    • Run the application and watch the console for errors from renamed commands and stricter validation
    • Check handlers that were registered more than once and relied on running more than once
    • Check code that watched for Enter with onKey() to detect the end of an input() prompt
    • Check right-click and middle-click behavior, since those buttons no longer produce clicks
    • Test gamepad stick movement and tune the dead zone under the new radial model
    • Listen to the overall mix with the limiter on, and adjust bus volumes if needed
  8. ADOPT NEW FEATURES

    • Use setBusVolume() for separate music and effects volume controls
    • Stream long music files with the stream flag on loadAudio()
    • Use gamepad button and axis names in place of numeric indices
    • Add onWheel() handlers for zooming and scrolling interfaces
    • Load the sound-advanced plugin when you need synthesis, bus effects, presets, or recording

TECHNICAL DETAILS

SOUND MIXING MODEL

  • sound() plays on the sfx bus, play() on music, and playAudio() on audio
  • master is the mix of all three buses, after setVolume() and before the limiter
  • The limiter soft-clips output above 0.9 and is on by default
  • On iOS, the mute switch silences audio files as well as synthesized sound, and game audio mixes with audio from other apps

VOICE AND TIMING MODEL

  • Samples, synthesized sounds, and PLAY notes draw from one voice budget
  • Looping samples are never stolen automatically; a rejected sound returns a completed ID
  • After the main thread stalls, a sound late by up to 25 ms starts at its place in the timeline with a short fade in
  • Later PLAY notes and delayed one-shots are skipped, and a looping sample moves to where it would be
  • Suspending the audio context keeps a song’s position

INPUT HANDLER MODEL

  • Handlers are identified by mode and function across the keyboard, pointer, and gamepad plugins
  • State is updated before handlers run, and a throwing handler does not stop the others
  • Data objects are live: frozen or updated in place until the next event
  • Releases caused by blur, a hidden page, or a stop command arrive through "up" handlers with cancelled: true

PLUGIN MODEL

  • Standalone plugin entry points are for the lite build; the full build already includes its plugins
  • Plugins can publish a service with provideService() and consume one with getService()
  • Failed plugin initialization leaves nothing installed
  • sound-advanced depends on the sound plugin and shares its audio context, master volume, limiter, and voice limits

UPGRADE CHECKLIST

  • ☐ Replace v2.x bundles, plugin bundles, and type definitions with v2.3 versions
  • ☐ Remove standalone plugin scripts that duplicate plugins in the full build
  • ☐ Rename keyboard, mouse, touch, press, click, and gamepad commands to camelCase
  • ☐ Rename setEnableContextMenu, setGamepadSensitivity, and the gamepad connection handlers
  • ☐ Change touch modes from "start" and "end" to "down" and "up"
  • ☐ Rewrite sound() envelopes with attackTime, decayTime, sustainLevel, and releaseTime
  • ☐ Rewrite PLAY envelopes and review songs that use MS or MN
  • ☐ Remove poolSize from loadAudio() and update stopAudio() object forms
  • ☐ Update checks for renamed audio error codes
  • ☐ Handle null from inMouse() and inPress(), and the array from inGamepad()
  • ☐ Review handlers registered more than once
  • ☐ Fix or remove unrecognized set() options
  • ☐ Review $.clearEvents(), setActionKeys(), and setPinchZoom() usage
  • ☐ Serve the application over HTTP or HTTPS when it loads audio
  • ☐ Test gamepad dead zones, click behavior, and the overall sound mix

SUMMARY

  • ✅ Audio instance control with pause, resume, and live volume, rate, and pan changes
  • ✅ Streamed audio loading for long music files
  • ✅ Sound buses with per-bus volume and an output limiter
  • ✅ ADSR envelopes for sound() and PLAY
  • ✅ New sound-advanced plugin for synthesis, effects, presets, instruments, and recording
  • ✅ Mouse-wheel input, gamepad button and axis names, and gamepad vibration
  • ✅ One set of conventions across the keyboard, pointer, and gamepad plugins
  • ✅ Plugin services for sharing an API between plugins
  • ✅ Stricter validation with specific error types and codes
  • ✅ Fixes for stuck keys, lost gamepad presses, pointer handler removal, and offscreen screens

Most v2.2 applications need code changes for this release. Rename the input commands, update sound() envelopes and loadAudio() calls, and review touch modes, set() options, and event cleanup before upgrading.

For detailed API documentation, visit: https://pijs.org/api