littlejsengine 1.8.9 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +17 -17
  2. package/build/littlejs.d.ts +352 -288
  3. package/build/littlejs.esm.js +695 -658
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +694 -658
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +629 -594
  8. package/examples/breakout/game.js +4 -4
  9. package/examples/breakout/index.html +3 -3
  10. package/examples/breakoutTutorial/index.html +2 -2
  11. package/examples/electron/game.js +1 -1
  12. package/examples/electron/index.html +2 -2
  13. package/examples/favicon.png +0 -0
  14. package/examples/js13k/build.js +1 -1
  15. package/examples/js13k/index.html +13 -13
  16. package/examples/module/game.js +1 -1
  17. package/examples/module/index.html +1 -1
  18. package/examples/particles/index.html +1 -1
  19. package/examples/platformer/game.js +6 -6
  20. package/examples/platformer/gameCharacter.js +293 -0
  21. package/examples/platformer/gameEffects.js +8 -5
  22. package/examples/platformer/gameObjects.js +13 -13
  23. package/examples/platformer/gamePlayer.js +9 -293
  24. package/examples/platformer/index.html +7 -6
  25. package/examples/puzzle/game.js +3 -2
  26. package/examples/puzzle/index.html +2 -2
  27. package/examples/starter/build.js +1 -1
  28. package/examples/starter/game.js +2 -2
  29. package/examples/starter/index.html +13 -13
  30. package/examples/stress/index.html +35 -27
  31. package/examples/typescript/index.html +1 -1
  32. package/index.d.ts +2094 -0
  33. package/package.json +1 -1
  34. package/src/engine.js +28 -28
  35. package/src/engineAudio.js +57 -57
  36. package/src/engineDebug.js +66 -65
  37. package/src/engineDraw.js +64 -73
  38. package/src/engineExport.js +1 -0
  39. package/src/engineInput.js +57 -40
  40. package/src/engineMedals.js +32 -29
  41. package/src/engineObject.js +42 -27
  42. package/src/engineParticles.js +98 -72
  43. package/src/engineRelease.js +1 -1
  44. package/src/engineSettings.js +22 -22
  45. package/src/engineTileLayer.js +66 -60
  46. package/src/engineUtilities.js +49 -49
  47. package/src/engineWebGL.js +112 -135
  48. package/src/jsconfig.json +10 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.8.9",
3
+ "version": "1.9.1",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "build/littlejs.esm.js",
6
6
  "types": "build/littlejs.esm.js",
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {String}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.8.9';
33
+ const engineVersion = '1.9.1';
34
34
 
35
35
  /** Frames per second to update objects
36
36
  * @type {Number}
@@ -71,14 +71,14 @@ let timeReal = 0;
71
71
 
72
72
  /** Is the game paused? Causes time and objects to not be updated
73
73
  * @type {Boolean}
74
- * @default 0
74
+ * @default false
75
75
  * @memberof Engine */
76
- let paused = 0;
76
+ let paused = false;
77
77
 
78
78
  /** Set if game is paused
79
- * @param {Boolean} paused
79
+ * @param {Boolean} isPaused
80
80
  * @memberof Engine */
81
- function setPaused(_paused) { paused = _paused; }
81
+ function setPaused(isPaused) { paused = isPaused; }
82
82
 
83
83
  // Frame time tracking
84
84
  let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
@@ -95,7 +95,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
95
95
  * @memberof Engine */
96
96
  function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
97
97
  {
98
- ASSERT(Array.isArray(imageSources)); // pass in images as array
98
+ ASSERT(Array.isArray(imageSources), 'pass in images as array');
99
99
 
100
100
  // internal update loop for engine
101
101
  function engineUpdate(frameTimeMS=0)
@@ -105,12 +105,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
105
105
  frameTimeLastMS = frameTimeMS;
106
106
  if (debug || showWatermark)
107
107
  averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
108
- const debugSpeedUp = debug && keyIsDown(107); // +
109
- const debugSpeedDown = debug && keyIsDown(109); // -
108
+ const debugSpeedUp = debug && keyIsDown('Equal'); // +
109
+ const debugSpeedDown = debug && keyIsDown('Minus'); // -
110
110
  if (debug) // +/- to speed/slow time
111
111
  frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1;
112
112
  timeReal += frameTimeDeltaMS / 1e3;
113
- frameTimeBufferMS += !paused * frameTimeDeltaMS;
113
+ frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
114
114
  if (!debugSpeedUp)
115
115
  frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
116
116
 
@@ -220,7 +220,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
220
220
  'user-select:none;' + // prevent mobile hold to select
221
221
  '-webkit-user-select:none;' + // compatibility for ios
222
222
  '-webkit-touch-callout:none'; // compatibility for ios
223
- document.body.style = styleBody;
223
+ document.body.style.cssText = styleBody;
224
224
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
225
225
  mainContext = mainCanvas.getContext('2d');
226
226
 
@@ -233,10 +233,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
233
233
  overlayContext = overlayCanvas.getContext('2d');
234
234
 
235
235
  // set canvas style
236
- const styleCanvas =
237
- 'position:absolute;' + // position
236
+ const styleCanvas = 'position:absolute;' + // position
238
237
  'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
239
- (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
238
+ (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
240
239
 
241
240
  // create promises for loading images
242
241
  const promises = imageSources.map((src, textureIndex)=>
@@ -256,13 +255,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
256
255
  showSplashScreen && promises.push(new Promise(resolve =>
257
256
  {
258
257
  let t = 0;
259
- console.log(`LittleJS Engine v${engineVersion}`);
258
+ console.log(`${engineName} Engine v${engineVersion}`);
260
259
  updateSplash();
261
260
  function updateSplash()
262
261
  {
263
262
  clearInput();
264
263
  drawEngineSplashScreen(t+=.01);
265
- t>1 ? resolve() : setTimeout(updateSplash,16);
264
+ t>1 ? resolve() : setTimeout(updateSplash, 16);
266
265
  }
267
266
  }));
268
267
 
@@ -323,7 +322,7 @@ function engineObjectsDestroy()
323
322
 
324
323
  /** Triggers a callback for each object within a given area
325
324
  * @param {Vector2} [pos] - Center of test area
326
- * @param {Number} [size] - Radius of circle if float, rectangle size if Vector2
325
+ * @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
327
326
  * @param {Function} [callbackFunction] - Calls this function on every object that passes the test
328
327
  * @param {Array} [objects=engineObjects] - List of objects to check
329
328
  * @memberof Engine */
@@ -334,7 +333,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
334
333
  for (const o of objects)
335
334
  callbackFunction(o);
336
335
  }
337
- else if (size.x != undefined) // bounding box test
336
+ else if (typeof size === 'object') // bounding box test
338
337
  {
339
338
  for (const o of objects)
340
339
  isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
@@ -352,23 +351,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
352
351
 
353
352
  function drawEngineSplashScreen(t)
354
353
  {
355
- const x = mainContext;
356
- const w = mainCanvas.width = innerWidth;
357
- const h = mainCanvas.height = innerHeight;
354
+ const x = overlayContext;
355
+ const w = overlayCanvas.width = innerWidth;
356
+ const h = overlayCanvas.height = innerHeight;
357
+
358
358
  {
359
359
  // background
360
360
  const p3 = percent(t, 1, .8);
361
361
  const p4 = percent(t, 0, .5);
362
362
  const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
363
- g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3));
364
- g.addColorStop(1,hsl(0,0,0,p3));
363
+ g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3).toString());
364
+ g.addColorStop(1,hsl(0,0,0,p3).toString());
365
365
  x.save();
366
366
  x.fillStyle = g;
367
367
  x.fillRect(0,0,w,h);
368
368
  }
369
369
 
370
370
  // draw LittleJS logo...
371
-
372
371
  const rect = (X, Y, W, H, C)=>
373
372
  {
374
373
  x.beginPath();
@@ -393,7 +392,7 @@ function drawEngineSplashScreen(t)
393
392
  C ? x.fill() : x.stroke();
394
393
  };
395
394
  const color = (c=0, l=0) =>
396
- hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
395
+ hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
397
396
  const alpha = wave(1,1,t);
398
397
  const p = percent(alpha, .1, .5);
399
398
 
@@ -403,7 +402,7 @@ function drawEngineSplashScreen(t)
403
402
  x.scale(size,size);
404
403
  x.translate(-40,-35);
405
404
  x.lineJoin = x.lineCap = 'round';
406
- x.lineWidth = 1+p;
405
+ x.lineWidth = .1 + p*1.9;
407
406
 
408
407
  // drawing effect
409
408
  const p2 = percent(alpha,.1,1);
@@ -428,7 +427,7 @@ function drawEngineSplashScreen(t)
428
427
 
429
428
  // little stack
430
429
  rect(37,14,9,6,color(3,2));
431
- rect(37,14,4,6,color(3,3));
430
+ rect(37,14,4.5,6,color(3,3));
432
431
  rect(37,14,9,6);
433
432
 
434
433
  // big stack
@@ -472,7 +471,8 @@ function drawEngineSplashScreen(t)
472
471
  x.lineTo(53+(1+i*2.9)*p,40);
473
472
  x.lineTo(53+(4+i*3.5)*p,54);
474
473
  x.fillStyle = color(0,i%2+2);
475
- x.fill() || i%2 && x.stroke();
474
+ x.fill();
475
+ i%2 && x.stroke();
476
476
  }
477
477
 
478
478
  // wheels
@@ -495,7 +495,7 @@ function drawEngineSplashScreen(t)
495
495
  x.font = '900 16px arial';
496
496
  x.textAlign = 'center';
497
497
  x.textBaseline = 'top';
498
- x.lineWidth = 1+p*3;
498
+ x.lineWidth = .1+p*3.9;
499
499
  let w2 = 0;
500
500
  for (let i=0; i<s.length; ++i)
501
501
  w2 += x.measureText(s[i]).width;
@@ -54,13 +54,13 @@ class Sound
54
54
 
55
55
  /** Play the sound
56
56
  * @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
57
- * @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
58
- * @param {Number} [pitch=1] - How much to scale pitch by (also adjusted by this.randomness)
59
- * @param {Number} [randomnessScale=1] - How much to scale randomness
60
- * @param {Boolean} [loop=0] - Should the sound loop
57
+ * @param {Number} [volume] - How much to scale volume by (in addition to range fade)
58
+ * @param {Number} [pitch] - How much to scale pitch by (also adjusted by this.randomness)
59
+ * @param {Number} [randomnessScale] - How much to scale randomness
60
+ * @param {Boolean} [loop] - Should the sound loop
61
61
  * @return {AudioBufferSourceNode} - The audio source node
62
62
  */
63
- play(pos, volume=1, pitch=1, randomnessScale=1, loop=0)
63
+ play(pos, volume=1, pitch=1, randomnessScale=1, loop=false)
64
64
  {
65
65
  if (!soundEnable || !this.sampleChannels) return;
66
66
 
@@ -93,8 +93,13 @@ class Sound
93
93
  {
94
94
  if (this.source)
95
95
  this.source.stop();
96
- this.source = 0;
96
+ this.source = undefined;
97
97
  }
98
+
99
+ /** Get source of most recent instance of this sound that was played
100
+ * @return {AudioBufferSourceNode}
101
+ */
102
+ getSource() { return this.source; }
98
103
 
99
104
  /** Play the sound as a note with a semitone offset
100
105
  * @param {Number} semitoneOffset - How many semitones to offset pitch
@@ -110,12 +115,7 @@ class Sound
110
115
  */
111
116
  getDuration()
112
117
  { return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
113
-
114
- /** Check if the last instance of this sound is playing
115
- * @return {Boolean} - True if the sound is playing
116
- */
117
- isPlaying() { return this.source && !this.source.ended; }
118
-
118
+
119
119
  /** Check if sound is loading, for sounds fetched from a url
120
120
  * @return {Boolean} - True if sound is loading and not ready to play
121
121
  */
@@ -136,13 +136,13 @@ class SoundWave extends Sound
136
136
  {
137
137
  /** Create a sound object and cache the wave file for later use
138
138
  * @param {String} filename - Filename of audio file to load
139
- * @param {Number} [randomness=0] - How much to randomize frequency each time sound plays
139
+ * @param {Number} [randomness] - How much to randomize frequency each time sound plays
140
140
  * @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
141
141
  * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
142
142
  */
143
143
  constructor(filename, randomness=0, range, taper)
144
144
  {
145
- super(0, range, taper);
145
+ super(undefined, range, taper);
146
146
  this.randomness = randomness;
147
147
 
148
148
  if (!soundEnable) return;
@@ -196,11 +196,11 @@ let soundDecoderContext; // audio context used only to decode audio files
196
196
  class Music extends Sound
197
197
  {
198
198
  /** Create a music object and cache the zzfx music samples for later use
199
- * @param {Array} zzfxMusic - Array of zzfx music parameters
199
+ * @param {[Array, Array, Array, Number]} zzfxMusic - Array of zzfx music parameters
200
200
  */
201
201
  constructor(zzfxMusic)
202
202
  {
203
- super();
203
+ super(undefined);
204
204
 
205
205
  if (!soundEnable) return;
206
206
  this.randomness = 0;
@@ -213,17 +213,17 @@ class Music extends Sound
213
213
  * @param {Boolean} [loop=1] - True if the music should loop
214
214
  * @return {AudioBufferSourceNode} - The audio source node
215
215
  */
216
- playMusic(volume, loop = 1)
217
- { return super.play(0, volume, 1, 1, loop); }
216
+ playMusic(volume, loop = false)
217
+ { return super.play(undefined, volume, 1, 1, loop); }
218
218
  }
219
219
 
220
220
  /** Play an mp3, ogg, or wav audio from a local file or url
221
221
  * @param {String} url - Location of sound file to play
222
- * @param {Number} [volume=1] - How much to scale volume by
223
- * @param {Boolean} [loop=1] - True if the music should loop
222
+ * @param {Number} [volume] - How much to scale volume by
223
+ * @param {Boolean} [loop] - True if the music should loop
224
224
  * @return {HTMLAudioElement} - The audio element for this sound
225
225
  * @memberof Audio */
226
- function playAudioFile(url, volume=1, loop=1)
226
+ function playAudioFile(url, volume=1, loop=false)
227
227
  {
228
228
  if (!soundEnable) return;
229
229
 
@@ -237,9 +237,9 @@ function playAudioFile(url, volume=1, loop=1)
237
237
  /** Speak text with passed in settings
238
238
  * @param {String} text - The text to speak
239
239
  * @param {String} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
240
- * @param {Number} [volume=1] - How much to scale volume by
241
- * @param {Number} [rate=1] - How quickly to speak
242
- * @param {Number} [pitch=1] - How much to change the pitch by
240
+ * @param {Number} [volume] - How much to scale volume by
241
+ * @param {Number} [rate] - How quickly to speak
242
+ * @param {Number} [pitch] - How much to change the pitch by
243
243
  * @return {SpeechSynthesisUtterance} - The utterance that was spoken
244
244
  * @memberof Audio */
245
245
  function speak(text, language='', volume=1, rate=1, pitch=1)
@@ -266,7 +266,7 @@ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
266
266
 
267
267
  /** Get frequency of a note on a musical scale
268
268
  * @param {Number} semitoneOffset - How many semitones away from the root note
269
- * @param {Number} [rootNoteFrequency=220] - Frequency at semitone offset 0
269
+ * @param {Number} [rootFrequency=220] - Frequency at semitone offset 0
270
270
  * @return {Number} - The frequency of the note
271
271
  * @memberof Audio */
272
272
  function getNoteFrequency(semitoneOffset, rootFrequency=220)
@@ -280,14 +280,14 @@ let audioContext;
280
280
 
281
281
  /** Play cached audio samples with given settings
282
282
  * @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
283
- * @param {Number} [volume=1] - How much to scale volume by
284
- * @param {Number} [rate=1] - The playback rate to use
285
- * @param {Number} [pan=0] - How much to apply stereo panning
286
- * @param {Boolean} [loop=0] - True if the sound should loop when it reaches the end
283
+ * @param {Number} [volume] - How much to scale volume by
284
+ * @param {Number} [rate] - The playback rate to use
285
+ * @param {Number} [pan] - How much to apply stereo panning
286
+ * @param {Boolean} [loop] - True if the sound should loop when it reaches the end
287
287
  * @param {Number} [sampleRate=44100] - Sample rate for the sound
288
288
  * @return {AudioBufferSourceNode} - The audio node of the sound played
289
289
  * @memberof Audio */
290
- function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate=zzfxR)
290
+ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR)
291
291
  {
292
292
  if (!soundEnable) return;
293
293
 
@@ -327,7 +327,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate
327
327
  }
328
328
 
329
329
  ///////////////////////////////////////////////////////////////////////////////
330
- // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force
330
+ // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.1 by Frank Force
331
331
 
332
332
  /** Generate and play a ZzFX sound
333
333
  *
@@ -343,27 +343,27 @@ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
343
343
  const zzfxR = 44100;
344
344
 
345
345
  /** Generate samples for a ZzFX sound
346
- * @param {Number} [volume=1] - Volume scale (percent)
347
- * @param {Number} [randomness=.05] - How much to randomize frequency (percent Hz)
348
- * @param {Number} [frequency=220] - Frequency of sound (Hz)
349
- * @param {Number} [attack=0] - Attack time, how fast sound starts (seconds)
350
- * @param {Number} [sustain=0] - Sustain time, how long sound holds (seconds)
351
- * @param {Number} [release=.1] - Release time, how fast sound fades out (seconds)
352
- * @param {Number} [shape=0] - Shape of the sound wave
353
- * @param {Number} [shapeCurve=1] - Squarenes of wave (0=square, 1=normal, 2=pointy)
354
- * @param {Number} [slide=0] - How much to slide frequency (kHz/s)
355
- * @param {Number} [deltaSlide=0] - How much to change slide (kHz/s/s)
356
- * @param {Number} [pitchJump=0] - Frequency of pitch jump (Hz)
357
- * @param {Number} [pitchJumpTime=0] - Time of pitch jump (seconds)
358
- * @param {Number} [repeatTime=0] - Resets some parameters periodically (seconds)
359
- * @param {Number} [noise=0] - How much random noise to add (percent)
360
- * @param {Number} [modulation=0] - Frequency of modulation wave, negative flips phase (Hz)
361
- * @param {Number} [bitCrush=0] - Resamples at a lower frequency in (samples*100)
362
- * @param {Number} [delay=0] - Overlap sound with itself for reverb and flanger effects (seconds)
363
- * @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
364
- * @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
365
- * @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
366
- * @param {Number} [filter=0] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
346
+ * @param {Number} [volume] - Volume scale (percent)
347
+ * @param {Number} [randomness] - How much to randomize frequency (percent Hz)
348
+ * @param {Number} [frequency] - Frequency of sound (Hz)
349
+ * @param {Number} [attack] - Attack time, how fast sound starts (seconds)
350
+ * @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
351
+ * @param {Number} [release] - Release time, how fast sound fades out (seconds)
352
+ * @param {Number} [shape] - Shape of the sound wave
353
+ * @param {Number} [shapeCurve] - Squarenes of wave (0=square, 1=normal, 2=pointy)
354
+ * @param {Number} [slide] - How much to slide frequency (kHz/s)
355
+ * @param {Number} [deltaSlide] - How much to change slide (kHz/s/s)
356
+ * @param {Number} [pitchJump] - Frequency of pitch jump (Hz)
357
+ * @param {Number} [pitchJumpTime] - Time of pitch jump (seconds)
358
+ * @param {Number} [repeatTime] - Resets some parameters periodically (seconds)
359
+ * @param {Number} [noise] - How much random noise to add (percent)
360
+ * @param {Number} [modulation] - Frequency of modulation wave, negative flips phase (Hz)
361
+ * @param {Number} [bitCrush] - Resamples at a lower frequency in (samples*100)
362
+ * @param {Number} [delay] - Overlap sound with itself for reverb and flanger effects (seconds)
363
+ * @param {Number} [sustainVolume] - Volume level for sustain (percent)
364
+ * @param {Number} [decay] - Decay time, how long to reach sustain after attack (seconds)
365
+ * @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (precent)
366
+ * @param {Number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
367
367
  * @return {Array} - Array of audio samples
368
368
  * @memberof Audio
369
369
  */
@@ -411,7 +411,7 @@ function zzfxG
411
411
  if (!(++c%(bitCrush*100|0))) // bit crush
412
412
  {
413
413
  s = shape? shape>1? shape>2? shape>3? // wave shape
414
- Math.sin(t*t) : // 4 noise
414
+ Math.sin(t**3) : // 4 noise
415
415
  clamp(Math.tan(t),1,-1): // 3 tan
416
416
  1-(2*t/PI2%2+2)%2: // 2 saw
417
417
  1-4*abs(Math.round(t/PI2)-t/PI2): // 1 triangle
@@ -468,7 +468,7 @@ function zzfxG
468
468
  * @param {Array} instruments - Array of ZzFX sound paramaters
469
469
  * @param {Array} patterns - Array of pattern data
470
470
  * @param {Array} sequence - Array of pattern indexes
471
- * @param {Number} [BPM=125] - Playback speed of the song in BPM
471
+ * @param {Number} [BPM] - Playback speed of the song in BPM
472
472
  * @return {Array} - Left and right channel sample data
473
473
  * @memberof Audio */
474
474
  function zzfxM(instruments, patterns, sequence, BPM = 125)
@@ -507,10 +507,10 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
507
507
  patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
508
508
 
509
509
  // check if there are more channels
510
- hasMore ||= !!patterns[patternIndex][channelIndex];
510
+ hasMore |= patterns[patternIndex][channelIndex]&&1;
511
511
 
512
512
  // get next offset, use the length of first channel
513
- nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - !notFirstBeat) * beatLength;
513
+ nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - (notFirstBeat?0:1)) * beatLength;
514
514
  // for each beat in pattern, plus one extra if end of sequence
515
515
  isSequenceEnd = sequenceIndex == sequence.length - 1;
516
516
  for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i) {
@@ -526,7 +526,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
526
526
  for (j = 0; j < beatLength && notFirstBeat;
527
527
 
528
528
  // fade off attenuation at end of beat if stopping note, prevents clicking
529
- j++ > beatLength - 99 && stop ? attenuation += (attenuation < 1) / 99 : 0
529
+ j++ > beatLength - 99 && stop && attenuation < 1? attenuation += 1 / 99 : 0
530
530
  ) {
531
531
  // copy sample to stereo buffers with panning
532
532
  sample = (1 - attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;