From 63960b5fafa49e5472610eda62463d903bc4ab9b Mon Sep 17 00:00:00 2001 From: Dorian Date: Sun, 8 Mar 2026 17:10:59 +0000 Subject: [PATCH] =?UTF-8?q?feat:=209=20diverse=20brawl=20variants=20?= =?UTF-8?q?=E2=80=94=20knockdowns,=20wall=20bounces,=20suplexes,=20haymake?= =?UTF-8?q?rs,=20dizzy=20staggers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add knockdown, wall-bounce, dizzy-stagger, suplex, ping-pong, ground-pound, haymaker, body-check brawl variants with dizzy stars, angle physics, and screen-edge impacts. Fix missing synthStyle on music tracks 10. Co-Authored-By: Claude Opus 4.6 --- frontend/src/game/FightScene.ts | 590 +++++++++++++++++---- frontend/src/game/sounds.ts | 882 +++++++++++++++++++++----------- 2 files changed, 1079 insertions(+), 393 deletions(-) diff --git a/frontend/src/game/FightScene.ts b/frontend/src/game/FightScene.ts index 92ab93f..dabe35b 100644 --- a/frontend/src/game/FightScene.ts +++ b/frontend/src/game/FightScene.ts @@ -9136,57 +9136,491 @@ export async function createFightScene(config: FightSceneConfig) { }, // === PHYSICAL CONTACT SYSTEMS === - // Brawl exchange: fighters close distance and trade rapid blows at close range - async _brawlExchange(winningSide: 'a' | 'b' | null, intensity: number) { + // Helper: spawn dizzy stars orbiting a fighter's head + _spawnDizzyStars(fighter: any, duration: number) { + const stars: any[] = [] + const starChars = ['*', '\u2605', '\u00d7', '!', '?'] + const starColors = ['#ffff00', '#ffffff', '#ff4444', '#44ff44', '#ff88ff'] + for (let i = 0; i < 4; i++) { + const s = k.add([ + k.text(starChars[i % starChars.length], { size: 8 }), + k.pos(fighter.pos.x, fighter.pos.y - 55), + k.color(safeColor(k, starColors[i % starColors.length])), + k.opacity(0.9), k.z(55), k.anchor('center'), + ]) + stars.push(s) + } + const startTime = k.time() + const cancel = k.onUpdate(() => { + const elapsed = k.time() - startTime + if (elapsed > duration || !fighter.exists()) { + stars.forEach(s => { if (s.exists()) s.destroy() }) + cancel.cancel() + return + } + for (let i = 0; i < stars.length; i++) { + if (!stars[i].exists()) continue + const angle = elapsed * 4 + (i * Math.PI * 2) / stars.length + stars[i].pos.x = fighter.pos.x + Math.cos(angle) * 22 + stars[i].pos.y = fighter.pos.y - 55 + Math.sin(angle) * 8 + stars[i].opacity = Math.max(0, 1 - elapsed / duration) + } + }) + }, + + // Helper: reset both fighters to home positions + async _resetPositions() { const fA = k.get('fighterA')[0] const fB = k.get('fighterB')[0] if (!fA || !fB) return - - // Close distance — both fighters move to center - const midX = W / 2 - const contactA = midX - 30 - const contactB = midX + 30 - sfxZoomWhoosh() - await Promise.all([ - k.tween(fA.pos.x, contactA, 0.12, (v) => { fA.pos.x = v }, k.easings.easeOutQuad), - k.tween(fB.pos.x, contactB, 0.12, (v) => { fB.pos.x = v }, k.easings.easeOutQuad), - ]) - - // Trade blows — number of hits scales with intensity - const hitCount = 4 + Math.floor(intensity * 6) + Math.floor(Math.random() * 3) // 4-13 hits - const anims = ['attack', 'kick', 'special', 'attack', 'kick'] as const - const hitColors = ['#ff2d7b', '#ff6600', '#ffcc00', '#00f0ff', '#ff2d2d', '#39ff14', '#ffffff', '#b83dff'] - - for (let i = 0; i < hitCount; i++) { - const aAttacks = winningSide === 'a' ? Math.random() < 0.65 : winningSide === 'b' ? Math.random() < 0.35 : Math.random() < 0.5 - const attacker = aAttacks ? fA : fB - const defender = aAttacks ? fB : fA - const anim = anims[i % anims.length] - - attacker.play(anim) - sfxPunch() - defender.play('hit') - k.shake(2 + Math.floor(intensity * 3)) - - // Sparks at contact point - const sparkX = (contactA + contactB) / 2 + (Math.random() - 0.5) * 20 - const sparkY = GROUND_Y - 20 - Math.random() * 30 - spawnSparks(sparkX, sparkY, 3, hitColors[i % hitColors.length]) - - // Small positional shifts during brawl — fighters push each other - const pushDir = aAttacks ? 1 : -1 - defender.pos.x += pushDir * (3 + Math.random() * 4) - - // Timing varies — faster hits at higher intensity - await k.wait(0.04 + (1 - intensity) * 0.04) - } - - // After brawl: both stagger apart - fA.play('idle'); fB.play('idle') await Promise.all([ k.tween(fA.pos.x, HOME_A, 0.2, (v) => { fA.pos.x = v }, k.easings.easeOutQuad), k.tween(fB.pos.x, HOME_B, 0.2, (v) => { fB.pos.x = v }, k.easings.easeOutQuad), + k.tween(fA.pos.y, GROUND_Y, 0.15, (v) => { fA.pos.y = v }), + k.tween(fB.pos.y, GROUND_Y, 0.15, (v) => { fB.pos.y = v }), ]) + fA.angle = 0; fB.angle = 0; fA.opacity = 1; fB.opacity = 1 + fA.play('idle'); fB.play('idle') + }, + + // 0: RAPID BRAWL — close distance, trade rapid blows + async _brawlRapid(winningSide: 'a' | 'b' | null, intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const midX = W / 2 + sfxZoomWhoosh() + await Promise.all([ + k.tween(fA.pos.x, midX - 30, 0.12, (v) => { fA.pos.x = v }, k.easings.easeOutQuad), + k.tween(fB.pos.x, midX + 30, 0.12, (v) => { fB.pos.x = v }, k.easings.easeOutQuad), + ]) + const hitCount = 4 + Math.floor(intensity * 6) + Math.floor(Math.random() * 3) + const anims = ['attack', 'kick', 'special', 'attack', 'kick'] as const + const hitColors = ['#ff2d7b', '#ff6600', '#ffcc00', '#00f0ff', '#ff2d2d', '#39ff14', '#ffffff', '#b83dff'] + for (let i = 0; i < hitCount; i++) { + const aHits = winningSide === 'a' ? Math.random() < 0.65 : winningSide === 'b' ? Math.random() < 0.35 : Math.random() < 0.5 + const atk = aHits ? fA : fB; const def = aHits ? fB : fA + atk.play(anims[i % anims.length]); sfxPunch(); def.play('hit') + k.shake(2 + Math.floor(intensity * 3)) + spawnSparks(midX + (Math.random() - 0.5) * 20, GROUND_Y - 20 - Math.random() * 30, 3, hitColors[i % hitColors.length]) + def.pos.x += (aHits ? 1 : -1) * (3 + Math.random() * 4) + await k.wait(0.04 + (1 - intensity) * 0.04) + } + await this._resetPositions() + }, + + // 1: KNOCKDOWN BRAWL — combo ends with loser knocked flat, has to get back up + async _brawlKnockdown(winningSide: 'a' | 'b' | null, intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB) + const loser = winner === fA ? fB : fA + const dir = winner === fA ? 1 : -1 + + // Rush in, quick combo + sfxZoomWhoosh() + const contactX = loser.pos.x - dir * 40 + await k.tween(winner.pos.x, contactX, 0.1, (v) => { winner.pos.x = v }, k.easings.easeInQuad) + const hits = 3 + Math.floor(intensity * 3) + for (let i = 0; i < hits; i++) { + winner.play(i % 2 === 0 ? 'attack' : 'kick'); sfxPunch() + loser.play('hit'); k.shake(3 + i) + spawnSparks(loser.pos.x, loser.pos.y - 20 - Math.random() * 20, 3, '#ff6600') + loser.pos.x += dir * 5 + await k.wait(0.05) + } + // Final hit — KNOCKDOWN + winner.play('special'); sfxCritical(); sfxExplosion() + loser.play('knockback'); k.shake(12) + spawnSparks(loser.pos.x, loser.pos.y - 25, 12, '#ff2d2d') + screenFlash('#ff2d2d', 0.1) + if (Math.random() < 0.5) sfxRandomComedy() + // Loser falls to ground — scale Y to flatten, rotate + await Promise.all([ + k.tween(loser.pos.x, loser.pos.x + dir * 80, 0.25, (v) => { loser.pos.x = v }, k.easings.easeOutQuad), + k.tween(loser.pos.y, GROUND_Y + 5, 0.2, (v) => { loser.pos.y = v }, k.easings.easeInQuad), + k.tween(0, dir * 90, 0.25, (v) => { loser.angle = v }), + ]) + sfxBonk(); k.shake(6) + spawnEmoteText(loser.pos.x, loser.pos.y - 20, 'DOWN!', '#ff4444') + // Loser lies there for a beat + await k.wait(0.4 + (1 - intensity) * 0.4) + // Slowly get back up — stagger, wobble + this._spawnDizzyStars(loser, 1.5) + spawnEmoteText(loser.pos.x, loser.pos.y - 40, '...ugh', '#aaaaaa') + await k.tween(loser.angle, 0, 0.3, (v) => { loser.angle = v }, k.easings.easeOutBack) + loser.play('hit') + // Stagger wobble while getting up + for (let i = 0; i < 3; i++) { + await k.tween(loser.pos.x, loser.pos.x + (i % 2 === 0 ? -12 : 12), 0.12, (v) => { loser.pos.x = v }) + } + await k.wait(0.2) + await this._resetPositions() + }, + + // 2: WALL BOUNCE — punch sends fighter flying into screen edge, bounces off + async _brawlWallBounce(winningSide: 'a' | 'b' | null, intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB) + const loser = winner === fA ? fB : fA + const dir = winner === fA ? 1 : -1 + const wallX = dir === 1 ? W - 15 : 15 + + // Rush in for the big hit + sfxZoomWhoosh() + await k.tween(winner.pos.x, loser.pos.x - dir * 40, 0.1, (v) => { winner.pos.x = v }, k.easings.easeInQuad) + winner.play('special'); sfxCritical() + await k.wait(0.06) + loser.play('knockback'); k.shake(15) + spawnSparks(loser.pos.x, loser.pos.y - 25, 15, '#ff2d7b') + screenFlash('#ffffff', 0.08) + + // Loser flies across to wall + await Promise.all([ + k.tween(loser.pos.x, wallX, 0.15, (v) => { loser.pos.x = v }, k.easings.easeInQuad), + k.tween(loser.pos.y, GROUND_Y - 30, 0.08, (v) => { loser.pos.y = v }, k.easings.easeOutQuad).then(() => + k.tween(loser.pos.y, GROUND_Y, 0.07, (v) => { loser.pos.y = v }, k.easings.easeInQuad) + ), + ]) + // WALL IMPACT + sfxExplosion(); k.shake(20) + spawnShockwave(wallX, GROUND_Y, '#ff6600') + spawnSparks(wallX, GROUND_Y - 40, 15, '#ffcc00') + screenFlash('#ff6600', 0.12) + sfxBoneCrack() + spawnEmoteText(wallX, GROUND_Y - 70, 'WALL!', '#ff6600') + + // Bounce off wall — loser rebounds back toward center + loser.play('hit') + const bounceX = wallX - dir * (60 + Math.random() * 40) + await Promise.all([ + k.tween(loser.pos.x, bounceX, 0.2, (v) => { loser.pos.x = v }, k.easings.easeOutQuad), + k.tween(loser.pos.y, GROUND_Y - 50, 0.1, (v) => { loser.pos.y = v }, k.easings.easeOutQuad).then(() => + k.tween(loser.pos.y, GROUND_Y, 0.1, (v) => { loser.pos.y = v }, k.easings.easeInQuad) + ), + k.tween(0, dir * -360, 0.2, (v) => { loser.angle = v }), + ]) + loser.angle = 0 + sfxBonk(); k.shake(5) + + // Dizzy stagger after wall bounce + this._spawnDizzyStars(loser, 1.2) + loser.play('hit') + for (let w = 0; w < 4; w++) { + await k.tween(loser.pos.x, loser.pos.x + (w % 2 === 0 ? -10 : 10), 0.1, (v) => { loser.pos.x = v }) + } + await k.wait(0.3) + winner.play('win'); spawnEmoteText(winner.pos.x, winner.pos.y - 55, 'GET REKT', '#39ff14') + await k.wait(0.3) + await this._resetPositions() + }, + + // 3: DIZZY STAGGER — sustained combo leaves loser wobbling around the screen + async _brawlDizzyStagger(winningSide: 'a' | 'b' | null, intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB) + const loser = winner === fA ? fB : fA + const dir = winner === fA ? 1 : -1 + + // Rush in, big combo + sfxZoomWhoosh() + await k.tween(winner.pos.x, loser.pos.x - dir * 35, 0.1, (v) => { winner.pos.x = v }, k.easings.easeInQuad) + const combo = 5 + Math.floor(intensity * 4) + for (let i = 0; i < combo; i++) { + winner.play(['attack', 'kick', 'special'][i % 3]); sfxPunch() + loser.play('hit'); k.shake(3 + Math.floor(i * 0.5)) + spawnSparks((winner.pos.x + loser.pos.x) / 2, GROUND_Y - 25 - Math.random() * 20, 3, i % 2 === 0 ? '#ff2d7b' : '#ffcc00') + loser.pos.x += dir * 4 + await k.wait(0.04) + } + // Final uppercut + winner.play('special'); sfxCritical(); k.shake(10) + spawnSparks(loser.pos.x, loser.pos.y - 30, 10, '#ffe14d') + await k.tween(loser.pos.y, GROUND_Y - 40, 0.1, (v) => { loser.pos.y = v }, k.easings.easeOutQuad) + await k.tween(loser.pos.y, GROUND_Y, 0.1, (v) => { loser.pos.y = v }, k.easings.easeInQuad) + sfxBonk() + + // DIZZY PHASE — loser staggers around aimlessly + this._spawnDizzyStars(loser, 2.5) + loser.play('hit') + spawnEmoteText(loser.pos.x, loser.pos.y - 50, '...wha?', '#ffff88') + winner.play('idle') + // Stagger path — zigzag drunkenly + const staggerPoints = [ + loser.pos.x + dir * 30, loser.pos.x - dir * 50, + loser.pos.x + dir * 20, loser.pos.x - dir * 40, + loser.pos.x + dir * 10, + ] + for (let s = 0; s < staggerPoints.length; s++) { + await Promise.all([ + k.tween(loser.pos.x, staggerPoints[s], 0.2, (v) => { loser.pos.x = v }), + k.tween(loser.angle, (s % 2 === 0 ? 10 : -10), 0.2, (v) => { loser.angle = v }), + ]) + if (s % 2 === 0) sfxBoing() + } + loser.angle = 0 + // Shake it off + spawnEmoteText(loser.pos.x, loser.pos.y - 40, '...ok', '#88ff88') + await k.wait(0.3) + await this._resetPositions() + }, + + // 4: SUPLEX SLAM — grab, lift overhead, slam to ground with bounce + async _brawlSuplex(attackerSide: 'a' | 'b', intensity: number) { + const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] + const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] + if (!attacker || !defender) return + const dir = attackerSide === 'a' ? 1 : -1 + + // Rush in and grab + sfxZoomWhoosh() + await k.tween(attacker.pos.x, defender.pos.x - dir * 30, 0.1, (v) => { attacker.pos.x = v }, k.easings.easeInQuad) + attacker.play('special'); sfxBlock() + await k.wait(0.1) + // Lift defender overhead + attacker.play('attack') + const liftX = attacker.pos.x + await Promise.all([ + k.tween(defender.pos.x, liftX + dir * 5, 0.15, (v) => { defender.pos.x = v }), + k.tween(defender.pos.y, GROUND_Y - 120, 0.2, (v) => { defender.pos.y = v }, k.easings.easeOutQuad), + k.tween(0, 180, 0.2, (v) => { defender.angle = v }), + ]) + sfxPowerUp() + spawnEmoteText(attacker.pos.x, attacker.pos.y - 70, 'SUPLEX!', '#ff4444') + await k.wait(0.25) + // SLAM DOWN + sfxExplosion() + await Promise.all([ + k.tween(defender.pos.y, GROUND_Y + 5, 0.1, (v) => { defender.pos.y = v }, k.easings.easeInQuad), + k.tween(defender.angle, 360, 0.1, (v) => { defender.angle = v }), + ]) + defender.angle = 0; defender.play('knockback') + k.shake(22); screenFlash('#ff2d2d', 0.15) + spawnShockwave(defender.pos.x, GROUND_Y, '#ff2d2d') + spawnSparks(defender.pos.x, GROUND_Y - 10, 20, '#ff6600') + sfxBoneCrack() + // Ground bounce + await k.tween(defender.pos.y, GROUND_Y - 40, 0.1, (v) => { defender.pos.y = v }, k.easings.easeOutQuad) + sfxBoing() + await k.tween(defender.pos.y, GROUND_Y, 0.1, (v) => { defender.pos.y = v }, k.easings.easeInQuad) + sfxBonk(); k.shake(5) + // Bounce 2 (smaller) + await k.tween(defender.pos.y, GROUND_Y - 15, 0.08, (v) => { defender.pos.y = v }, k.easings.easeOutQuad) + await k.tween(defender.pos.y, GROUND_Y, 0.08, (v) => { defender.pos.y = v }, k.easings.easeInQuad) + // Dizzy on the ground + this._spawnDizzyStars(defender, 1.5) + defender.play('hit') + await k.wait(0.5) + attacker.play('win') + await k.wait(0.3) + await this._resetPositions() + }, + + // 5: PING PONG VOLLEY — fighters knock each other back and forth across the screen + async _brawlPingPong(winningSide: 'a' | 'b' | null, intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const volleys = 3 + Math.floor(intensity * 3) + Math.floor(Math.random() * 2) // 3-8 + + // Start in center + sfxZoomWhoosh() + await Promise.all([ + k.tween(fA.pos.x, W / 2 - 30, 0.1, (v) => { fA.pos.x = v }, k.easings.easeOutQuad), + k.tween(fB.pos.x, W / 2 + 30, 0.1, (v) => { fB.pos.x = v }, k.easings.easeOutQuad), + ]) + + for (let v = 0; v < volleys; v++) { + const aHits = v % 2 === 0 + const atk = aHits ? fA : fB + const def = aHits ? fB : fA + const pDir = aHits ? 1 : -1 + const pushDist = 50 + Math.random() * 40 + + atk.play(v % 3 === 0 ? 'attack' : v % 3 === 1 ? 'kick' : 'special') + sfxPunch(); def.play('hit'); k.shake(5 + v) + spawnSparks((atk.pos.x + def.pos.x) / 2, GROUND_Y - 30, 5, v % 2 === 0 ? '#ff2d7b' : '#00f0ff') + + // Defender flies back + await Promise.all([ + k.tween(def.pos.x, def.pos.x + pDir * pushDist, 0.1, (v) => { def.pos.x = v }, k.easings.easeOutQuad), + k.tween(def.pos.y, GROUND_Y - 25, 0.05, (v) => { def.pos.y = v }, k.easings.easeOutQuad).then(() => + k.tween(def.pos.y, GROUND_Y, 0.05, (v) => { def.pos.y = v }, k.easings.easeInQuad) + ), + ]) + sfxBonk() + // Attacker chases + await k.tween(atk.pos.x, def.pos.x - pDir * 35, 0.08, (v) => { atk.pos.x = v }, k.easings.easeInQuad) + await k.wait(0.03) + } + // Final hit sends loser sliding + const finalAtk = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (volleys % 2 === 0 ? fA : fB) + const finalDef = finalAtk === fA ? fB : fA + finalAtk.play('special'); sfxCritical(); k.shake(12) + finalDef.play('knockback') + spawnSparks(finalDef.pos.x, finalDef.pos.y - 25, 12, '#ffe14d') + if (Math.random() < 0.5) sfxRandomComedy() + await k.wait(0.3) + await this._resetPositions() + }, + + // 6: GROUND AND POUND — attacker pins defender, hits them on the ground + async _brawlGroundPound(attackerSide: 'a' | 'b', intensity: number) { + const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] + const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] + if (!attacker || !defender) return + const dir = attackerSide === 'a' ? 1 : -1 + + // Rush in and tackle + sfxZoomWhoosh() + await k.tween(attacker.pos.x, defender.pos.x - dir * 30, 0.08, (v) => { attacker.pos.x = v }, k.easings.easeInQuad) + attacker.play('attack'); sfxPunch() + defender.play('knockback'); k.shake(8) + // Defender falls to ground (flatten angle) + await Promise.all([ + k.tween(defender.pos.x, defender.pos.x + dir * 20, 0.15, (v) => { defender.pos.x = v }), + k.tween(0, dir * 85, 0.15, (v) => { defender.angle = v }), + ]) + sfxBonk() + // Attacker moves on top + await k.tween(attacker.pos.x, defender.pos.x - dir * 15, 0.08, (v) => { attacker.pos.x = v }) + + // Ground pound — rapid hits on downed fighter + const poundHits = 4 + Math.floor(intensity * 5) + for (let i = 0; i < poundHits; i++) { + attacker.play(i % 2 === 0 ? 'attack' : 'kick') + sfxPunch() + k.shake(2 + Math.floor(i * 0.4)) + spawnSparks(defender.pos.x + (Math.random() - 0.5) * 20, defender.pos.y - 10, 2, i % 2 === 0 ? '#ff2d7b' : '#ff6600') + defender.pos.y += (i % 2 === 0 ? -2 : 2) // jostle + await k.wait(0.06 + (1 - intensity) * 0.04) + } + // Attacker gets up, backs off + attacker.play('win') + sfxRandomComedy() + await k.wait(0.3) + // Defender slowly gets up + this._spawnDizzyStars(defender, 1.8) + await k.tween(defender.angle, 0, 0.4, (v) => { defender.angle = v }, k.easings.easeOutBack) + defender.play('hit') + spawnEmoteText(defender.pos.x, defender.pos.y - 45, '...ouch', '#ff8888') + await k.wait(0.4) + await this._resetPositions() + }, + + // 7: HAYMAKER — slow dramatic wind-up, pause, devastating single hit with knockdown + async _brawlHaymaker(attackerSide: 'a' | 'b', _intensity: number) { + const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] + const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] + if (!attacker || !defender) return + const dir = attackerSide === 'a' ? 1 : -1 + + // Slow walk toward defender + attacker.play('kick') + await k.tween(attacker.pos.x, defender.pos.x - dir * 45, 0.4, (v) => { attacker.pos.x = v }, k.easings.easeInOutQuad) + // Wind-up — lean back + attacker.play('special') + sfxDrumRoll() + await k.tween(attacker.pos.x, attacker.pos.x - dir * 20, 0.3, (v) => { attacker.pos.x = v }, k.easings.easeOutQuad) + spawnEmoteText(attacker.pos.x, attacker.pos.y - 55, '...', '#ffffff') + await k.wait(0.5) // dramatic pause + // HAYMAKER + sfxCritical(); sfxExplosion() + attacker.play('attack') + await k.tween(attacker.pos.x, defender.pos.x - dir * 25, 0.04, (v) => { attacker.pos.x = v }, k.easings.easeInQuad) + defender.play('knockback'); k.shake(25) + screenFlash('#ffffff', 0.15) + spawnSparks(defender.pos.x, defender.pos.y - 25, 20, '#ff2d2d') + spawnShockwave(defender.pos.x, GROUND_Y, '#ff2d2d') + sfxVineBoom() + // Defender flies across screen with spin + const flyX = defender.pos.x + dir * 150 + await Promise.all([ + k.tween(defender.pos.x, Math.min(W - 10, Math.max(10, flyX)), 0.25, (v) => { defender.pos.x = v }, k.easings.easeOutQuad), + k.tween(defender.pos.y, GROUND_Y - 80, 0.12, (v) => { defender.pos.y = v }, k.easings.easeOutQuad).then(() => + k.tween(defender.pos.y, GROUND_Y, 0.13, (v) => { defender.pos.y = v }, k.easings.easeInQuad) + ), + k.tween(0, dir * 720, 0.25, (v) => { defender.angle = v }), + ]) + defender.angle = 0; sfxBonk(); k.shake(8) + // Knockdown — rotate flat + await k.tween(0, dir * 90, 0.15, (v) => { defender.angle = v }) + spawnEmoteText(defender.pos.x, defender.pos.y - 20, 'K.O.?!', '#ff4444') + this._spawnDizzyStars(defender, 2.0) + await k.wait(0.6) + // Get up slowly + await k.tween(defender.angle, 0, 0.35, (v) => { defender.angle = v }, k.easings.easeOutBack) + defender.play('hit') + await k.wait(0.3) + attacker.play('idle') + await this._resetPositions() + }, + + // 8: BODY CHECK BOUNCER — both charge, collide, one bounces off the other + async _brawlBodyCheck(winningSide: 'a' | 'b' | null, _intensity: number) { + const fA = k.get('fighterA')[0] + const fB = k.get('fighterB')[0] + if (!fA || !fB) return + const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB) + const loser = winner === fA ? fB : fA + const dir = winner === fA ? 1 : -1 + const midX = W / 2 + + // Both charge at each other + fA.play('special'); fB.play('special') + sfxZoomWhoosh() + await Promise.all([ + k.tween(fA.pos.x, midX - 20, 0.12, (v) => { fA.pos.x = v }, k.easings.easeInQuad), + k.tween(fB.pos.x, midX + 20, 0.12, (v) => { fB.pos.x = v }, k.easings.easeInQuad), + ]) + // COLLISION + sfxCritical(); k.shake(18) + spawnSparks(midX, GROUND_Y - 40, 15, '#ffffff') + screenFlash('#ffffff', 0.1) + spawnShockwave(midX, GROUND_Y, '#ffcc00') + // Winner holds ground, loser bounces off + winner.play('win') + loser.play('knockback') + const bounceX = loser === fA ? -20 : W + 20 + await Promise.all([ + k.tween(loser.pos.x, bounceX, 0.2, (v) => { loser.pos.x = v }, k.easings.easeOutQuad), + k.tween(loser.pos.y, GROUND_Y - 60, 0.1, (v) => { loser.pos.y = v }, k.easings.easeOutQuad).then(() => + k.tween(loser.pos.y, GROUND_Y, 0.1, (v) => { loser.pos.y = v }, k.easings.easeInQuad) + ), + k.tween(0, (loser === fA ? -1 : 1) * 540, 0.2, (v) => { loser.angle = v }), + ]) + // Off screen! Slide back in + loser.angle = 0; loser.play('hit') + sfxSlideWhistleDown() + const slideFrom = loser === fA ? -30 : W + 30 + loser.pos.x = slideFrom + await k.tween(loser.pos.x, loser === fA ? HOME_A : HOME_B, 0.4, (v) => { loser.pos.x = v }, k.easings.easeOutBounce) + this._spawnDizzyStars(loser, 1.5) + sfxBoing() + spawnEmoteText(loser.pos.x, loser.pos.y - 50, '...what happened?', '#ffff88') + await k.wait(0.5) + await this._resetPositions() + }, + + // Dispatch: pick a random physical exchange variant + async _brawlExchange(winningSide: 'a' | 'b' | null, intensity: number) { + const variant = Math.floor(Math.random() * 9) + switch (variant) { + case 0: return this._brawlRapid(winningSide, intensity) + case 1: return this._brawlKnockdown(winningSide, intensity) + case 2: return this._brawlWallBounce(winningSide, intensity) + case 3: return this._brawlDizzyStagger(winningSide, intensity) + case 4: return this._brawlSuplex(winningSide === 'a' ? 'a' : winningSide === 'b' ? 'b' : (Math.random() > 0.5 ? 'a' : 'b'), intensity) + case 5: return this._brawlPingPong(winningSide, intensity) + case 6: return this._brawlGroundPound(winningSide === 'a' ? 'a' : winningSide === 'b' ? 'b' : (Math.random() > 0.5 ? 'a' : 'b'), intensity) + case 7: return this._brawlHaymaker(winningSide === 'a' ? 'a' : winningSide === 'b' ? 'b' : (Math.random() > 0.5 ? 'a' : 'b'), intensity) + case 8: return this._brawlBodyCheck(winningSide, intensity) + } }, // Clinch combo: grapple at close range, one fighter dominates with sustained contact @@ -9194,63 +9628,33 @@ export async function createFightScene(config: FightSceneConfig) { const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] if (!attacker || !defender) return - const dir = attackerSide === 'a' ? 1 : -1 const origAX = attackerSide === 'a' ? HOME_A : HOME_B const origDX = attackerSide === 'a' ? HOME_B : HOME_A const clinchX = (origAX + origDX) / 2 - // Rush in to clinch sfxZoomWhoosh() attacker.play('attack') await k.tween(attacker.pos.x, clinchX - dir * 25, 0.1, (v) => { attacker.pos.x = v }, k.easings.easeInQuad) await k.tween(defender.pos.x, clinchX + dir * 25, 0.08, (v) => { defender.pos.x = v }, k.easings.easeOutQuad) - - // Clinch phase — sustained close-range hits with body contact - const clinchHits = 3 + Math.floor(intensity * 5) + Math.floor(Math.random() * 3) // 3-11 hits + const clinchHits = 3 + Math.floor(intensity * 5) + Math.floor(Math.random() * 3) const movePool = ['attack', 'kick', 'special'] as const - for (let i = 0; i < clinchHits; i++) { - const move = movePool[i % movePool.length] - attacker.play(move) - sfxPunch() + attacker.play(movePool[i % movePool.length]); sfxPunch() await k.wait(0.03) - defender.play('hit') - k.shake(2 + Math.floor(i * 0.5)) - - // Defender gets pushed back slightly, attacker follows - defender.pos.x += dir * 4 - attacker.pos.x += dir * 3 - - // Sparks at the contact point between fighters - spawnSparks( - (attacker.pos.x + defender.pos.x) / 2, - GROUND_Y - 25 - Math.random() * 20, - 2 + Math.floor(intensity * 3), - i % 2 === 0 ? '#ff2d7b' : '#ffcc00', - ) - - // Defender occasionally blocks or fights back (30% chance) + defender.play('hit'); k.shake(2 + Math.floor(i * 0.5)) + defender.pos.x += dir * 4; attacker.pos.x += dir * 3 + spawnSparks((attacker.pos.x + defender.pos.x) / 2, GROUND_Y - 25 - Math.random() * 20, 2 + Math.floor(intensity * 3), i % 2 === 0 ? '#ff2d7b' : '#ffcc00') if (Math.random() < 0.3) { - defender.play(movePool[Math.floor(Math.random() * movePool.length)]) - sfxBlock() - attacker.play('hit') - k.shake(3) - attacker.pos.x -= dir * 5 - await k.wait(0.04) + defender.play(movePool[Math.floor(Math.random() * movePool.length)]); sfxBlock() + attacker.play('hit'); k.shake(3); attacker.pos.x -= dir * 5; await k.wait(0.04) } - await k.wait(0.04 + (1 - intensity) * 0.03) } - - // Break apart — attacker shoves defender away - sfxKick() - defender.play('knockback') - k.shake(6 + Math.floor(intensity * 6)) + sfxKick(); defender.play('knockback'); k.shake(6 + Math.floor(intensity * 6)) spawnSparks(defender.pos.x, defender.pos.y - 25, 8, '#ff2d2d') await k.tween(defender.pos.x, origDX + dir * 30, 0.15, (v) => { defender.pos.x = v }, k.easings.easeOutQuad) attacker.play('idle') - await Promise.all([ k.tween(attacker.pos.x, origAX, 0.2, (v) => { attacker.pos.x = v }, k.easings.easeOutQuad), k.tween(defender.pos.x, origDX, 0.25, (v) => { defender.pos.x = v }, k.easings.easeInOutQuad), @@ -9263,30 +9667,18 @@ export async function createFightScene(config: FightSceneConfig) { const defender = k.get(defenderSide === 'a' ? 'fighterA' : 'fighterB')[0] const attacker = k.get(defenderSide === 'a' ? 'fighterB' : 'fighterA')[0] if (!defender || !attacker) return - const dir = defenderSide === 'a' ? 1 : -1 const origDX = defenderSide === 'a' ? HOME_A : HOME_B - - // Quick retaliation — defender rushes in - defender.play('attack') - sfxZoomWhoosh() - const contactX = attacker.pos.x - dir * 35 - await k.tween(defender.pos.x, contactX, 0.08, (v) => { defender.pos.x = v }, k.easings.easeInQuad) - - // 2-3 quick counter hits + defender.play('attack'); sfxZoomWhoosh() + await k.tween(defender.pos.x, attacker.pos.x - dir * 35, 0.08, (v) => { defender.pos.x = v }, k.easings.easeInQuad) const counterHits = 2 + Math.floor(Math.random() * 2) for (let i = 0; i < counterHits; i++) { - defender.play(i % 2 === 0 ? 'attack' : 'kick') - sfxPunch() - attacker.play('hit') - k.shake(4) + defender.play(i % 2 === 0 ? 'attack' : 'kick'); sfxPunch() + attacker.play('hit'); k.shake(4) spawnSparks(attacker.pos.x + (Math.random() - 0.5) * 15, attacker.pos.y - 20 - Math.random() * 15, 3, '#00f0ff') await k.wait(0.05) } - - // Return - attacker.play('idle') - defender.play('idle') + attacker.play('idle'); defender.play('idle') await k.tween(defender.pos.x, origDX, 0.15, (v) => { defender.pos.x = v }, k.easings.easeOutQuad) }, diff --git a/frontend/src/game/sounds.ts b/frontend/src/game/sounds.ts index 4967664..9a01a73 100644 --- a/frontend/src/game/sounds.ts +++ b/frontend/src/game/sounds.ts @@ -1971,12 +1971,282 @@ function hihat(dest: AudioNode, t: number, open: boolean = false) { src.start(t); src.stop(t + dur) } -// === TRACK DATA === -// Each track has: bass, lead, arp patterns (128 steps = 8 bars x 16 steps), chords (8 bars), drums (128 steps), bpm +// === GENRE-SPECIFIC SYNTH VARIANTS === +// Pulse bass — sharp square wave, chiptune/heroic/bouncy +function pulseBass(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'square' + osc.frequency.value = freq + const g = c.createGain() + g.gain.setValueAtTime(0.18, t) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) +} + +// Slap bass — short punchy sine with fast decay, funky/speed +function slapBass(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'sine' + osc.frequency.setValueAtTime(freq * 1.5, t) + osc.frequency.exponentialRampToValueAtTime(freq, t + 0.02) + const g = c.createGain() + g.gain.setValueAtTime(0.3, t) + g.gain.exponentialRampToValueAtTime(0.001, t + Math.min(dur, 0.12)) + osc.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) +} + +// Sub bass — deep pure sine, atmospheric/emotional +function subBass(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'sine' + osc.frequency.value = freq + const g = c.createGain() + g.gain.setValueAtTime(0.25, t) + g.gain.setValueAtTime(0.25, t + dur * 0.8) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) +} + +// Distorted bass — sawtooth through waveshaper, metal/boss +function distortedBass(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'sawtooth' + osc.frequency.value = freq + const dist = c.createWaveShaper() + const curve = new Float32Array(256) + for (let i = 0; i < 256; i++) { const x = (i / 128) - 1; curve[i] = Math.tanh(x * 3) } + dist.curve = curve + const g = c.createGain() + g.gain.setValueAtTime(0.22, t) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(dist); dist.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) + const sub = c.createOscillator() + sub.type = 'sine' + sub.frequency.value = freq * 0.5 + const sg = c.createGain() + sg.gain.setValueAtTime(0.12, t) + sg.gain.exponentialRampToValueAtTime(0.001, t + dur * 0.7) + sub.connect(sg); sg.connect(dest) + sub.start(t); sub.stop(t + dur) +} + +// Organ bass — square + sine harmonics, gothic +function organBass(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + for (const [waveType, vol, mult] of [['square', 0.1, 1], ['sine', 0.12, 2], ['sine', 0.06, 3]] as [OscillatorType, number, number][]) { + const osc = c.createOscillator() + osc.type = waveType + osc.frequency.value = freq * mult + const g = c.createGain() + g.gain.setValueAtTime(vol, t) + g.gain.setValueAtTime(vol, t + dur * 0.85) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) + } +} + +// Sharp lead — detuned square waves, chiptune/ninja +function sharpLead(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'square' + osc.frequency.value = freq + const osc2 = c.createOscillator() + osc2.type = 'square' + osc2.frequency.value = freq + osc2.detune.value = 6 + const g = c.createGain() + g.gain.setValueAtTime(0.1, t) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); osc2.connect(g); g.connect(dest) + if (delayNode) g.connect(delayNode) + osc.start(t); osc.stop(t + dur) + osc2.start(t); osc2.stop(t + dur) +} + +// Smooth lead — gentle sine FM with vibrato, emotional/JRPG +function smoothLead(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const car = c.createOscillator() + car.type = 'triangle' + car.frequency.value = freq + const mod = c.createOscillator() + mod.type = 'sine' + mod.frequency.value = freq * 1.5 + const modG = c.createGain() + modG.gain.value = 40 + mod.connect(modG); modG.connect(car.frequency) + const vib = c.createOscillator() + vib.type = 'sine' + vib.frequency.value = 5 + const vibG = c.createGain() + vibG.gain.value = 4 + vib.connect(vibG); vibG.connect(car.frequency) + const g = c.createGain() + g.gain.setValueAtTime(0.001, t) + g.gain.linearRampToValueAtTime(0.14, t + dur * 0.15) + g.gain.setValueAtTime(0.14, t + dur * 0.7) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + car.connect(g); g.connect(dest) + if (delayNode) g.connect(delayNode) + car.start(t); car.stop(t + dur) + mod.start(t); mod.stop(t + dur) + vib.start(t); vib.stop(t + dur) +} + +// Aggressive lead — heavy FM sawtooth, metal/boss +function aggressiveLead(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const car = c.createOscillator() + car.type = 'sawtooth' + car.frequency.value = freq + const mod = c.createOscillator() + mod.type = 'square' + mod.frequency.value = freq * 3 + const modG = c.createGain() + modG.gain.value = 300 + mod.connect(modG); modG.connect(car.frequency) + const car2 = c.createOscillator() + car2.type = 'sawtooth' + car2.frequency.value = freq + car2.detune.value = -15 + const g = c.createGain() + g.gain.setValueAtTime(0.13, t) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + car.connect(g); car2.connect(g); g.connect(dest) + if (delayNode) g.connect(delayNode) + car.start(t); car.stop(t + dur) + car2.start(t); car2.stop(t + dur) + mod.start(t); mod.stop(t + dur) +} + +// Wah lead — filtered sawtooth with bandpass sweep, funky/speed +function wahLead(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'sawtooth' + osc.frequency.value = freq + const filter = c.createBiquadFilter() + filter.type = 'bandpass' + filter.Q.value = 5 + filter.frequency.setValueAtTime(freq * 2, t) + filter.frequency.exponentialRampToValueAtTime(freq * 8, t + dur * 0.3) + filter.frequency.exponentialRampToValueAtTime(freq * 2, t + dur) + const g = c.createGain() + g.gain.setValueAtTime(0.2, t) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(filter); filter.connect(g); g.connect(dest) + if (delayNode) g.connect(delayNode) + osc.start(t); osc.stop(t + dur) +} + +// Gothic lead — vibrato sawtooth + triangle overtone, Castlevania +function gothicLead(freq: number, dur: number, dest: AudioNode, t: number) { + const c = getCtx() + const osc = c.createOscillator() + osc.type = 'sawtooth' + osc.frequency.value = freq + const vib = c.createOscillator() + vib.type = 'sine' + vib.frequency.value = 6.5 + const vibG = c.createGain() + vibG.gain.value = 8 + vib.connect(vibG); vibG.connect(osc.frequency) + const osc2 = c.createOscillator() + osc2.type = 'triangle' + osc2.frequency.value = freq * 2 + const g = c.createGain() + g.gain.setValueAtTime(0.12, t) + g.gain.setValueAtTime(0.12, t + dur * 0.75) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); g.connect(dest) + const g2 = c.createGain() + g2.gain.setValueAtTime(0.04, t) + g2.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc2.connect(g2); g2.connect(dest) + if (delayNode) g.connect(delayNode) + osc.start(t); osc.stop(t + dur) + osc2.start(t); osc2.stop(t + dur) + vib.start(t); vib.stop(t + dur) +} + +// Dark pad — filtered sawtooth, gothic/atmospheric/boss +function darkPad(freqs: number[], dur: number, dest: AudioNode, t: number) { + const c = getCtx() + for (const freq of freqs) { + const osc = c.createOscillator() + osc.type = 'sawtooth' + osc.frequency.value = freq + const filter = c.createBiquadFilter() + filter.type = 'lowpass' + filter.frequency.value = freq * 3 + filter.Q.value = 1 + const g = c.createGain() + g.gain.setValueAtTime(0.03, t) + g.gain.setValueAtTime(0.03, t + dur * 0.8) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(filter); filter.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) + } +} + +// Bright pad — square wave, heroic/bouncy +function brightPad(freqs: number[], dur: number, dest: AudioNode, t: number) { + const c = getCtx() + for (const freq of freqs) { + const osc = c.createOscillator() + osc.type = 'square' + osc.frequency.value = freq + const g = c.createGain() + g.gain.setValueAtTime(0.025, t) + g.gain.setValueAtTime(0.025, t + dur * 0.8) + g.gain.exponentialRampToValueAtTime(0.001, t + dur) + osc.connect(g); g.connect(dest) + osc.start(t); osc.stop(t + dur) + } +} + +// Style dispatch tables +type SynthStyle = 'epic' | 'gothic' | 'funky' | 'heroic' | 'metal' | 'emotional' | 'ninja' | 'speed' | 'atmospheric' | 'military' | 'bouncy' | 'boss' +type BassFunc = (freq: number, dur: number, dest: AudioNode, t: number) => void +type PadFunc = (freqs: number[], dur: number, dest: AudioNode, t: number) => void + +const STYLE_BASS: Record = { + epic: thickBass, gothic: organBass, funky: slapBass, heroic: pulseBass, + metal: distortedBass, emotional: subBass, ninja: pulseBass, speed: slapBass, + atmospheric: subBass, military: thickBass, bouncy: pulseBass, boss: distortedBass, +} +const STYLE_LEAD: Record = { + epic: fmLead, gothic: gothicLead, funky: wahLead, heroic: sharpLead, + metal: aggressiveLead, emotional: smoothLead, ninja: sharpLead, speed: wahLead, + atmospheric: smoothLead, military: fmLead, bouncy: sharpLead, boss: aggressiveLead, +} +const STYLE_ARP_TYPE: Record = { + epic: 'triangle', gothic: 'sawtooth', funky: 'square', heroic: 'square', + metal: 'sawtooth', emotional: 'triangle', ninja: 'square', speed: 'triangle', + atmospheric: 'sine', military: 'triangle', bouncy: 'square', boss: 'sawtooth', +} +const STYLE_PAD: Record = { + epic: chordPad, gothic: darkPad, funky: chordPad, heroic: brightPad, + metal: darkPad, emotional: chordPad, ninja: darkPad, speed: brightPad, + atmospheric: darkPad, military: chordPad, bouncy: brightPad, boss: darkPad, +} + +// === TRACK DATA === interface MusicTrack { name: string bpm: number + synthStyle: SynthStyle bass: number[] lead: number[] arp: number[] @@ -1984,399 +2254,423 @@ interface MusicTrack { drums: number[] } -// === COMPOSED TRACKS — Turrican-style emotionally charged fighting music === -// Hand-composed melodies with sweeping arcs, driving bass, shimmering arps - -// Track 1: "Genesis Block" — Am, 155 BPM — Epic sweeping main theme +// Track 1: "Desert Storm" — 200 BPM, A minor, Turrican-style epic sweep const track1: MusicTrack = { - name: 'Genesis Block', bpm: 155, + name: 'Desert Storm', bpm: 200, synthStyle: 'epic', bass: [ - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,165,220,165, - 175,0,349,0, 175,0,262,0, 175,0,349,0, 175,262,349,262, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,196, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,0, - 110,0,220,0, 110,0,165,0, 110,0,220,165, 110,165,220,165, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,220, - 82,0,165,0, 82,0,123,0, 82,0,165,123, 82,123,165,123, - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,0,0,0, + 110,0,220,0, 110,0,220,110, 110,0,220,0, 165,0,220,0, + 87,0,175,0, 87,0,175,87, 87,0,175,0, 131,0,175,0, + 73,0,147,0, 73,0,147,73, 73,0,147,0, 110,0,147,0, + 82,0,165,0, 82,0,165,82, 82,0,165,0, 123,0,165,0, + 110,0,220,110, 110,165,220,110, 110,0,220,110, 110,165,220,165, + 131,0,262,131, 131,196,262,131, 131,0,262,131, 131,196,262,196, + 87,0,175,87, 87,131,175,87, 87,0,175,87, 87,131,175,131, + 82,0,165,82, 82,123,165,82, 82,165,247,330, 165,82,0,0, ], lead: [ - 659,0,587,523, 494,0,523,587, 659,0,784,880, 784,659,587,523, - 698,0,659,587, 523,0,587,659, 698,0,880,1047, 880,698,659,587, - 523,0,659,784, 880,0,784,659, 1047,0,988,880, 784,659,523,659, - 784,0,880,988, 1175,0,988,880, 784,0,659,587, 523,494,440,0, - 659,0,784,880, 988,0,880,784, 659,0,587,659, 784,880,784,659, - 587,0,698,880, 1047,0,880,698, 1175,0,1047,880, 698,587,698,880, - 659,0,831,988, 1319,0,1175,988, 831,988,1319,831, 988,1319,1661,1319, - 880,0,784,659, 523,0,659,784, 880,0,1319,1047, 880,659,523,440, + 440,0,523,659, 880,0,659,523, 440,0,523,659, 784,880,784,659, + 349,0,440,523, 698,0,523,440, 349,0,440,523, 587,698,659,523, + 294,0,349,440, 587,0,440,349, 587,698,880,1047, 880,698,587,440, + 330,0,440,523, 659,0,784,880, 1047,0,880,784, 659,523,440,330, + 880,1047,1319,1760, 1319,0,1047,880, 784,659,880,1047, 1319,1047,880,659, + 1047,1319,1568,1319, 1047,784,659,784, 1047,1319,1568,2093, 1568,1319,1047,784, + 698,880,1047,1397, 1760,0,1397,1047, 880,698,880,1047, 1397,1047,880,698, + 659,784,880,1047, 880,784,659,523, 440,523,659,880, 659,0,0,0, ], arp: [ - 440,523,659,523, 440,523,659,880, 659,880,659,523, 440,659,523,440, - 349,440,523,440, 349,440,523,698, 523,698,523,440, 349,523,440,349, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 196,247,294,247, 196,247,294,392, 294,392,294,247, 196,294,247,196, - 440,523,659,880, 659,880,1047,880, 659,523,440,523, 659,880,659,523, - 294,349,440,349, 294,349,440,587, 440,587,440,349, 294,440,349,294, - 330,415,494,415, 330,415,494,659, 494,659,494,415, 330,494,415,330, - 440,523,659,523, 440,659,523,440, 659,523,440,330, 440,523,659,0, + 440,523,659,523, 440,523,659,880, 659,523,440,523, 659,880,659,523, + 349,440,523,440, 349,440,523,698, 523,440,349,440, 523,698,523,440, + 294,349,440,349, 294,349,440,587, 440,349,294,349, 440,587,440,349, + 330,415,523,415, 330,415,523,659, 523,415,330,415, 523,659,523,415, + 440,659,880,659, 440,523,659,880, 1047,880,659,523, 440,659,880,1047, + 523,784,1047,784, 523,659,784,1047, 1319,1047,784,659, 523,784,1047,1319, + 349,523,698,523, 349,440,523,698, 880,698,523,440, 349,523,698,880, + 330,523,659,523, 330,415,523,659, 880,659,523,415, 440,523,659,0, ], - // I-V-vi-IV in C major (classic pop anthem) - chords: [[262,330,392],[196,247,294],[220,262,330],[175,220,262],[262,330,392],[196,247,294],[220,262,330],[175,220,262]], + chords: [[220,262,330],[175,220,262],[147,175,220],[165,208,247],[220,262,330],[131,165,196],[175,220,262],[165,208,247]], drums: [ - 1,3,0,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,3,0,3, 2,0,4,0, - 1,3,0,3, 2,0,3,3, 1,3,0,3, 2,0,3,0, 1,3,0,3, 2,3,3,0, 1,3,0,3, 2,3,4,3, - 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,4,0, - 1,3,1,3, 2,3,3,0, 1,3,1,3, 2,3,3,3, 1,3,1,3, 2,3,1,3, 2,2,2,2, 1,0,0,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,4, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, + 1,0,3,0, 2,0,3,4, 1,0,3,0, 2,3,4,3, + 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,0,3,0, + 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,3,4,3, + 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,1,3, + 1,3,1,3, 2,3,4,3, 1,1,2,1, 2,2,2,2, ], } -// Track 2: "Lightning Strike" — Em, 165 BPM — Fast driving energy +// Track 2: "Vampire's Requiem" — 190 BPM, D minor, Castlevania gothic const track2: MusicTrack = { - name: 'Lightning Strike', bpm: 165, + name: "Vampire's Requiem", bpm: 190, synthStyle: 'gothic', bass: [ - 82,0,165,0, 82,0,123,0, 82,0,165,0, 82,123,165,123, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,196, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,147, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,220, - 82,0,165,0, 82,0,123,0, 82,0,165,123, 82,123,165,123, - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,165,220,165, - 123,0,247,0, 123,0,185,0, 123,0,247,185, 123,185,247,185, - 82,0,165,0, 82,0,123,0, 82,0,165,0, 82,0,0,0, + 147,0,294,147, 147,0,294,0, 147,0,294,147, 175,220,294,220, + 117,0,233,117, 117,0,233,0, 117,0,233,117, 147,175,233,175, + 98,0,196,98, 98,0,196,0, 98,0,196,98, 131,147,196,147, + 110,0,220,110, 110,0,220,0, 110,0,220,110, 139,165,220,165, + 147,294,147,294, 147,294,175,220, 147,294,147,294, 175,220,294,349, + 117,233,117,233, 117,233,175,233, 175,349,175,349, 175,349,220,262, + 98,196,98,196, 131,196,262,196, 98,196,98,196, 131,196,262,330, + 110,220,110,220, 139,220,277,220, 110,0,220,0, 110,0,0,0, ], lead: [ - 494,0,659,784, 988,0,784,659, 494,0,659,988, 784,659,494,392, - 523,0,659,784, 1047,0,784,659, 523,0,659,784, 1047,784,659,523, - 784,0,988,1175, 784,0,587,494, 784,0,988,1175, 1319,1175,988,784, - 587,0,740,880, 1175,0,880,740, 587,0,740,880, 1175,880,740,587, - 659,740,784,880, 988,0,880,784, 659,740,784,988, 1319,988,784,659, - 880,0,1047,1319, 880,0,659,523, 880,0,1047,1319, 1175,1047,880,659, - 988,0,1245,1480, 988,0,740,622, 988,1245,1480,1976, 1480,1245,988,740, - 659,0,784,988, 1319,0,988,784, 659,784,988,1319, 1568,1319,988,659, + 587,0,698,880, 1047,0,880,698, 587,0,440,349, 440,587,698,880, + 466,0,587,698, 932,0,698,587, 466,0,349,233, 349,466,587,698, + 392,0,494,587, 784,0,587,494, 392,0,330,262, 330,392,494,587, + 440,0,554,698, 880,0,698,554, 440,330,220,330, 440,554,698,880, + 1175,0,1047,880, 698,587,440,587, 698,880,1047,1175, 1397,1175,1047,880, + 932,0,880,698, 587,466,349,466, 587,698,880,932, 1047,932,880,698, + 784,0,698,587, 494,392,330,392, 494,587,698,784, 880,784,698,587, + 880,698,554,440, 554,440,330,220, 294,349,440,587, 698,0,0,0, ], arp: [ - 330,392,494,392, 330,392,494,659, 494,659,494,392, 330,494,392,330, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 392,494,587,494, 392,494,587,784, 587,784,587,494, 392,587,494,392, - 294,370,440,370, 294,370,440,587, 440,587,440,370, 294,440,370,294, - 330,392,494,659, 494,659,784,659, 494,392,330,392, 494,659,494,392, - 440,523,659,523, 440,523,659,880, 659,880,659,523, 440,659,523,440, - 494,622,740,622, 494,622,740,988, 740,988,740,622, 494,740,622,494, - 330,392,494,392, 330,494,392,330, 494,392,330,247, 330,392,494,0, + 294,349,440,349, 294,349,440,587, 440,349,294,349, 440,587,440,349, + 233,294,349,294, 233,294,349,466, 349,294,233,294, 349,466,349,294, + 196,262,330,262, 196,262,330,392, 330,262,196,262, 330,392,330,262, + 220,277,349,277, 220,277,349,440, 349,277,220,277, 349,440,349,277, + 294,440,587,440, 294,349,440,587, 698,587,440,349, 294,440,587,698, + 233,349,466,349, 233,294,349,466, 587,466,349,294, 233,349,466,587, + 196,330,392,330, 196,262,330,392, 494,392,330,262, 196,330,392,494, + 220,349,440,349, 220,277,349,440, 554,440,349,277, 220,349,440,0, ], - // i-VII-VI-v in E minor (Andalusian descent) - chords: [[165,196,247],[147,185,220],[131,165,196],[123,147,185],[165,196,247],[131,165,196],[147,185,220],[165,196,247]], + chords: [[147,175,220],[233,294,349],[196,233,294],[220,277,330],[147,175,220],[175,220,262],[233,294,349],[220,277,330]], drums: [ - 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,4,0, - 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,3,3,3, 2,3,4,3, - 1,0,3,0, 2,0,3,0, 1,3,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,4,0, - 1,3,3,0, 2,3,3,3, 1,3,1,3, 2,3,3,3, 1,3,1,3, 2,3,1,3, 2,2,2,2, 1,0,0,0, + 1,0,0,3, 2,0,0,3, 1,0,0,3, 2,0,0,3, + 1,0,0,3, 2,0,0,3, 1,0,0,3, 2,0,3,4, + 1,0,3,3, 2,0,0,3, 1,0,3,3, 2,0,0,3, + 1,0,3,3, 2,0,0,3, 1,0,3,3, 2,0,3,4, + 1,3,0,3, 2,0,3,3, 1,3,0,3, 2,0,3,3, + 1,3,0,3, 2,3,0,3, 1,3,0,3, 2,3,4,3, + 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,1,3, + 1,1,2,1, 2,1,2,1, 2,2,2,2, 2,2,2,2, ], } -// Track 3: "Cypherpunk" — Dm, 148 BPM — Dark mysterious atmosphere +// Track 3: "Street Heat" — 175 BPM, E minor, Streets of Rage funky const track3: MusicTrack = { - name: 'Cypherpunk', bpm: 148, + name: 'Street Heat', bpm: 175, synthStyle: 'funky', bass: [ - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,220, - 117,0,233,0, 117,0,175,0, 117,0,233,0, 117,175,233,175, - 175,0,349,0, 175,0,262,0, 175,0,349,0, 175,262,349,262, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,196, - 147,0,294,0, 147,0,220,0, 147,0,294,220, 147,220,294,220, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,147, - 110,0,220,0, 110,0,165,0, 110,0,220,165, 110,165,220,165, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,0,0,0, + 82,0,0,165, 0,0,82,0, 165,0,0,196, 0,165,0,82, + 110,0,0,220, 0,0,110,0, 220,0,0,262, 0,220,0,110, + 131,0,0,262, 0,0,131,0, 262,0,0,330, 0,262,0,131, + 123,0,0,247, 0,0,123,0, 247,0,0,294, 0,247,0,123, + 82,165,0,82, 0,165,82,0, 165,82,0,196, 165,0,82,165, + 110,220,0,110, 0,220,110,0, 220,110,0,262, 220,0,110,220, + 131,262,0,131, 0,262,131,0, 262,131,0,330, 262,0,131,262, + 123,247,0,123, 0,247,123,0, 247,0,0,0, 123,0,0,0, ], lead: [ - 587,0,698,0, 880,0,698,587, 523,0,587,698, 880,698,587,523, - 466,0,587,698, 932,0,698,587, 466,0,587,698, 932,880,698,587, - 698,0,880,1047, 698,0,523,440, 698,0,880,1047, 1175,1047,880,698, - 523,0,659,784, 1047,0,784,659, 523,0,659,784, 1047,932,784,659, - 587,698,880,1175, 1397,0,1175,880, 698,0,587,440, 587,698,880,1175, - 784,0,932,1175, 784,0,587,466, 784,932,1175,1568, 1175,932,784,587, - 880,0,1109,1319, 880,0,659,554, 880,1109,1319,1760, 1319,1109,880,659, - 587,0,698,880, 1175,0,880,698, 587,0,698,880, 1175,880,698,587, + 659,0,0,784, 0,0,659,0, 0,880,0,784, 0,659,0,0, + 880,0,0,1047, 0,0,880,0, 0,1175,0,1047, 0,880,0,0, + 1047,0,0,1175, 0,0,1319,0, 0,1175,0,1047, 0,880,784,0, + 988,0,0,880, 0,0,784,0, 0,659,0,784, 880,0,784,0, + 659,784,0,880, 1047,0,880,0, 659,0,784,880, 1047,0,880,659, + 880,1047,0,1175, 1319,0,1175,0, 880,0,1047,1175, 1319,0,1175,880, + 1047,1319,0,1568, 1760,0,1568,0, 1319,0,1047,880, 784,0,659,0, + 880,659,0,784, 659,0,523,0, 494,0,659,0, 784,0,0,0, ], arp: [ - 294,349,440,349, 294,349,440,587, 440,587,440,349, 294,440,349,294, - 233,294,349,294, 233,294,349,466, 349,466,349,294, 233,349,294,233, - 349,440,523,440, 349,440,523,698, 523,698,523,440, 349,523,440,349, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 294,349,440,587, 440,587,698,587, 440,349,294,349, 440,587,440,349, - 196,233,294,233, 196,233,294,392, 294,392,294,233, 196,294,233,196, - 220,277,330,277, 220,277,330,440, 330,440,330,277, 220,330,277,220, - 294,349,440,349, 294,440,349,294, 440,349,294,220, 294,349,440,0, + 330,0,392,0, 494,0,392,0, 330,0,494,0, 659,0,494,0, + 440,0,523,0, 659,0,523,0, 440,0,659,0, 880,0,659,0, + 523,0,659,0, 784,0,659,0, 523,0,784,0, 1047,0,784,0, + 494,0,587,0, 740,0,587,0, 494,0,740,0, 988,0,740,0, + 330,494,659,494, 330,392,494,659, 784,659,494,392, 330,494,659,784, + 440,659,880,659, 440,523,659,880, 1047,880,659,523, 440,659,880,1047, + 523,784,1047,784, 523,659,784,1047, 1319,1047,784,659, 523,784,1047,1319, + 494,740,988,740, 494,587,740,988, 740,0,0,0, 494,0,0,0, ], - // i-III-VI-V in D minor (dark dramatic) - chords: [[147,175,220],[175,220,262],[233,294,349],[220,277,330],[147,175,220],[175,220,262],[233,294,349],[147,175,220]], + chords: [[165,196,247],[220,262,330],[262,330,392],[247,294,370],[165,196,247],[196,247,294],[220,262,330],[247,294,370]], drums: [ - 1,0,0,3, 0,0,2,0, 1,0,0,3, 0,0,2,0, 1,0,0,3, 0,0,2,0, 1,0,3,3, 2,0,0,0, - 1,0,0,3, 0,0,2,0, 1,0,3,3, 2,0,0,0, 1,0,0,3, 0,0,2,0, 1,0,0,3, 2,0,4,0, - 1,0,3,3, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,4,0, - 1,0,3,0, 2,0,3,3, 1,3,3,0, 2,3,3,3, 1,3,1,3, 2,3,1,3, 2,2,4,2, 1,0,0,0, + 1,0,0,0, 2,0,0,3, 0,0,1,0, 2,0,3,0, + 1,0,0,0, 2,0,0,3, 0,0,1,0, 2,0,3,4, + 1,0,3,0, 2,0,0,3, 0,3,1,0, 2,0,3,0, + 1,0,3,0, 2,3,0,3, 0,3,1,0, 2,0,3,4, + 1,0,3,0, 2,3,0,3, 1,3,1,0, 2,0,3,0, + 1,3,3,0, 2,3,0,3, 1,3,1,0, 2,3,3,4, + 1,3,3,3, 2,3,0,3, 1,3,3,3, 2,3,1,3, + 1,1,2,1, 2,1,2,3, 1,1,2,2, 2,2,2,2, ], } -// Track 4: "Hash Storm" — Cm, 170 BPM — Intense boss battle energy +// Track 4: "Steel Resolve" — 210 BPM, C major, Mega Man heroic const track4: MusicTrack = { - name: 'Hash Storm', bpm: 170, + name: 'Steel Resolve', bpm: 210, synthStyle: 'heroic', bass: [ - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,196, - 104,0,208,0, 104,0,156,0, 104,0,208,0, 104,156,208,156, - 156,0,311,0, 156,0,233,0, 156,0,311,0, 156,233,311,233, - 117,0,233,0, 117,0,175,0, 117,0,233,0, 117,175,233,175, - 131,0,262,0, 131,0,196,0, 131,0,262,196, 131,196,262,196, - 175,0,349,0, 175,0,262,0, 175,0,349,0, 175,262,349,262, - 98,0,196,0, 98,0,147,0, 98,0,196,147, 98,147,196,147, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,0,0,0, + 131,0,262,131, 131,0,262,0, 131,0,196,0, 131,262,196,131, + 196,0,392,196, 196,0,392,0, 196,0,262,0, 196,392,262,196, + 220,0,440,220, 220,0,440,0, 220,0,330,0, 220,440,330,220, + 175,0,349,175, 175,0,349,0, 175,0,262,0, 175,349,262,175, + 131,262,131,262, 131,262,196,262, 196,392,196,392, 196,392,262,392, + 220,440,220,440, 220,440,330,440, 175,349,175,349, 175,349,262,349, + 131,196,262,330, 262,196,131,196, 196,262,330,392, 330,262,196,131, + 175,262,349,440, 349,262,175,131, 131,262,0,0, 131,0,0,0, ], lead: [ - 784,0,622,523, 392,0,523,622, 784,0,1047,1245, 1047,784,622,523, - 831,0,622,523, 415,0,523,622, 831,0,1047,1245, 1047,831,622,523, - 622,0,784,932, 1245,0,932,784, 622,0,784,932, 1245,932,784,622, - 932,0,1175,1397, 932,0,698,587, 932,1175,1397,1865, 1397,1175,932,698, - 523,622,784,1047, 1245,0,1047,784, 622,523,622,784, 1047,1245,1568,1245, - 698,0,831,1047, 1397,0,1047,831, 698,0,831,1047, 1397,1047,831,698, - 784,0,988,1175, 1568,0,1175,988, 784,988,1175,1568, 1976,1568,1175,988, - 1047,0,784,622, 523,0,622,784, 1047,0,1245,1568, 1047,784,622,523, + 523,659,784,1047, 784,659,523,659, 784,1047,1319,1047, 784,659,523,392, + 784,1047,1175,1319, 1175,1047,784,659, 523,659,784,1047, 1175,1047,784,659, + 880,1047,1175,1319, 1175,1047,880,784, 659,784,880,1047, 1175,1047,880,659, + 698,880,1047,1175, 1047,880,698,523, 440,523,698,880, 1047,880,698,523, + 1047,1319,1568,1319, 1047,784,659,784, 1047,1319,1568,2093, 1568,1319,1047,784, + 1175,1319,1568,1760, 1568,1319,1175,880, 659,880,1175,1319, 1568,1319,1175,880, + 880,1047,1175,1319, 1175,1047,880,784, 659,784,880,1047, 1175,1047,880,784, + 698,880,1047,1175, 1047,880,698,523, 523,659,784,1047, 784,0,0,0, ], arp: [ - 262,311,392,311, 262,311,392,523, 392,523,392,311, 262,392,311,262, - 208,262,311,262, 208,262,311,415, 311,415,311,262, 208,311,262,208, - 311,392,466,392, 311,392,466,622, 466,622,466,392, 311,466,392,311, - 233,294,349,294, 233,294,349,466, 349,466,349,294, 233,349,294,233, - 262,311,392,523, 392,523,622,523, 392,311,262,311, 392,523,392,311, - 349,415,523,415, 349,415,523,698, 523,698,523,415, 349,523,415,349, - 392,494,587,494, 392,494,587,784, 587,784,587,494, 392,587,494,392, - 262,311,392,311, 262,392,311,262, 392,311,262,196, 262,311,392,0, + 262,330,392,330, 262,330,392,523, 392,330,262,330, 392,523,392,330, + 392,494,587,494, 392,494,587,784, 587,494,392,494, 587,784,587,494, + 440,523,659,523, 440,523,659,880, 659,523,440,523, 659,880,659,523, + 349,440,523,440, 349,440,523,698, 523,440,349,440, 523,698,523,440, + 262,392,523,784, 523,392,262,392, 523,784,1047,784, 523,392,262,523, + 392,587,784,1047, 784,587,392,587, 784,1047,1319,1047, 784,587,392,784, + 440,659,880,659, 440,523,659,880, 1047,880,659,523, 440,659,880,1047, + 349,523,698,523, 349,440,523,698, 880,698,523,0, 262,523,0,0, ], - // i-iv-VII-III in C minor (aggressive power) - chords: [[131,156,196],[175,208,262],[233,294,349],[156,196,233],[131,156,196],[175,208,262],[233,294,349],[131,156,196]], + chords: [[262,330,392],[196,247,294],[220,262,330],[175,220,262],[262,330,392],[165,196,247],[175,220,262],[196,247,294]], drums: [ - 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,4,0, - 1,3,1,3, 2,0,3,3, 1,3,0,3, 2,3,3,0, 1,3,1,3, 2,3,3,0, 1,3,3,3, 2,3,4,3, - 1,3,1,3, 2,3,3,0, 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,0,3,3, 1,3,1,3, 2,3,4,0, - 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,1,3, 1,1,1,1, 2,2,2,2, 2,2,2,2, 1,0,0,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, + 1,0,3,0, 2,0,3,0, 1,3,3,0, 2,0,3,4, + 1,3,3,0, 2,0,3,0, 1,3,3,0, 2,0,3,0, + 1,3,3,0, 2,3,3,0, 1,3,3,0, 2,3,3,4, + 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,0,3,4, + 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,4,3, + 1,3,1,3, 2,3,1,3, 1,1,1,3, 2,1,1,3, + 1,1,1,1, 2,1,2,1, 2,2,2,1, 2,2,2,2, ], } -// Track 5: "Proof of Work" — Am, 145 BPM — Grinding determination +// Track 5: "Thunder Blade" — 230 BPM, B minor, Thunder Force metal shred const track5: MusicTrack = { - name: 'Proof of Work', bpm: 145, + name: 'Thunder Blade', bpm: 230, synthStyle: 'metal', bass: [ - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,165,220,0, - 82,0,165,0, 82,0,123,0, 82,0,165,0, 82,123,165,0, - 87,0,175,0, 87,0,131,0, 87,0,175,0, 87,131,175,0, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,0, - 110,0,220,0, 110,0,165,0, 110,0,220,165, 110,165,220,0, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,0, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,0, - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,0,0,0, + 123,123,247,123, 123,123,247,123, 123,123,247,0, 185,247,185,123, + 98,98,196,98, 98,98,196,98, 98,98,196,0, 147,196,147,98, + 82,82,165,82, 82,82,165,82, 82,82,165,0, 131,165,131,82, + 92,92,185,92, 92,92,185,92, 92,92,185,0, 139,185,139,92, + 123,247,123,247, 123,247,185,247, 123,247,123,247, 185,247,294,370, + 98,196,98,196, 98,196,147,196, 98,196,98,196, 147,196,247,294, + 82,165,82,165, 82,165,131,165, 82,165,82,165, 131,165,196,247, + 92,185,92,185, 92,185,139,185, 92,185,247,370, 247,185,0,0, ], lead: [ - 440,0,523,659, 880,0,659,523, 440,0,659,880, 1047,880,659,523, - 659,0,784,988, 659,0,494,392, 659,0,784,988, 1319,988,784,659, - 698,0,880,1047, 698,0,523,440, 698,880,1047,1397, 1047,880,698,523, - 659,0,784,1047, 659,0,523,392, 659,784,1047,1319, 1047,784,659,523, - 880,0,1047,1319, 880,0,659,523, 880,1047,1319,1760, 1319,1047,880,659, - 587,0,698,880, 1175,0,880,698, 587,698,880,1175, 1397,1175,880,698, - 784,0,988,1175, 784,0,587,494, 784,988,1175,1568, 1175,988,784,587, - 880,0,659,523, 440,0,523,659, 880,0,1047,1319, 880,659,523,440, + 988,0,988,1175, 1480,0,1175,988, 740,0,740,988, 1175,0,988,740, + 784,0,784,988, 1175,0,988,784, 587,0,587,784, 988,0,784,587, + 659,0,659,784, 988,0,784,659, 494,0,494,659, 784,0,659,494, + 740,0,740,880, 1109,0,880,740, 554,0,554,740, 880,0,740,554, + 988,1175,1480,1976, 1480,1175,988,1175, 1480,1976,1480,1175, 988,740,988,1175, + 784,988,1175,1568, 1175,988,784,988, 1175,1568,1175,988, 784,587,784,988, + 659,784,988,1319, 988,784,659,784, 988,1319,988,784, 659,494,659,784, + 1480,1175,988,740, 988,740,554,440, 554,740,988,1175, 1480,0,0,0, ], arp: [ - 440,523,659,523, 440,523,659,880, 659,880,659,523, 440,659,523,440, - 330,392,494,392, 330,392,494,659, 494,659,494,392, 330,494,392,330, - 349,440,523,440, 349,440,523,698, 523,698,523,440, 349,523,440,349, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 440,523,659,880, 659,880,1047,880, 659,523,440,523, 659,880,659,523, - 294,349,440,349, 294,349,440,587, 440,587,440,349, 294,440,349,294, - 392,494,587,494, 392,494,587,784, 587,784,587,494, 392,587,494,392, - 440,523,659,523, 440,659,523,440, 659,523,440,330, 440,523,659,0, + 247,370,494,370, 247,370,494,740, 494,370,247,370, 494,740,494,370, + 196,294,392,294, 196,294,392,587, 392,294,196,294, 392,587,392,294, + 165,247,330,247, 165,247,330,494, 330,247,165,247, 330,494,330,247, + 185,277,370,277, 185,277,370,554, 370,277,185,277, 370,554,370,277, + 247,494,740,494, 247,370,494,740, 988,740,494,370, 247,494,740,988, + 196,392,587,392, 196,294,392,587, 784,587,392,294, 196,392,587,784, + 165,330,494,330, 165,247,330,494, 659,494,330,247, 165,330,494,659, + 185,370,554,370, 185,277,370,554, 740,554,370,0, 247,370,0,0, ], - // i-v-iv-VII in A minor (grinding stubborn) - chords: [[220,262,330],[165,196,247],[147,175,220],[196,247,294],[220,262,330],[165,196,247],[147,175,220],[220,262,330]], + chords: [[247,294,370],[196,247,294],[165,196,247],[185,220,277],[247,294,370],[220,262,330],[196,247,294],[185,220,277]], drums: [ - 1,0,0,3, 2,0,0,3, 1,0,0,3, 2,0,0,3, 1,0,0,3, 2,0,0,3, 1,0,3,0, 2,0,0,0, - 1,0,0,3, 2,0,3,0, 1,0,0,3, 2,0,0,3, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,4,0, - 1,0,3,0, 2,0,3,0, 1,0,0,3, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,4,0, - 1,0,3,0, 2,0,3,3, 1,0,3,3, 2,3,3,3, 1,3,1,3, 2,3,1,3, 2,2,4,0, 1,0,0,0, + 1,1,1,3, 2,1,1,3, 1,1,1,3, 2,1,4,1, + 1,1,1,3, 2,1,1,3, 1,1,1,3, 2,1,4,1, + 1,1,1,1, 2,1,1,1, 1,1,4,1, 2,1,1,1, + 1,1,1,1, 2,1,4,1, 2,1,1,1, 2,2,4,1, + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,4,1, + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,4,1, + 1,1,1,1, 2,1,4,1, 1,1,1,1, 2,1,4,1, + 1,1,2,1, 2,1,2,1, 2,2,2,2, 2,2,2,2, ], } -// Track 6: "Satoshi's Dream" — Gm, 140 BPM — Beautiful melancholic melody +// Track 6: "Crystal Elegy" — 150 BPM, F major, JRPG emotional const track6: MusicTrack = { - name: "Satoshi's Dream", bpm: 140, + name: 'Crystal Elegy', bpm: 150, synthStyle: 'emotional', bass: [ - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,0, - 156,0,311,0, 156,0,233,0, 156,0,311,0, 156,233,311,0, - 117,0,233,0, 117,0,175,0, 117,0,233,0, 117,175,233,0, - 87,0,175,0, 87,0,131,0, 87,0,175,0, 87,131,175,0, - 98,0,196,0, 98,0,147,0, 98,0,196,147, 98,147,196,0, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,0, - 147,0,294,0, 147,0,220,0, 147,0,294,220, 147,220,294,0, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,0,0,0, + 87,0,175,0, 87,0,175,0, 87,0,175,0, 131,0,175,0, + 73,0,147,0, 73,0,147,0, 73,0,147,0, 110,0,147,0, + 117,0,233,0, 117,0,233,0, 117,0,233,0, 175,0,233,0, + 131,0,262,0, 131,0,262,0, 131,0,262,0, 196,0,262,0, + 87,175,87,175, 87,131,175,262, 175,131,87,131, 175,262,175,131, + 73,147,73,147, 73,110,147,220, 147,110,73,110, 147,220,147,110, + 117,233,117,233, 117,175,233,349, 233,175,117,175, 233,349,233,175, + 131,262,131,262, 131,196,262,330, 262,196,131,0, 131,0,0,0, ], lead: [ - 587,0,784,932, 1175,0,932,784, 587,0,466,587, 784,932,1175,932, - 622,0,784,932, 1245,0,932,784, 622,0,784,932, 1245,1175,932,784, - 932,0,1175,1397, 932,0,698,587, 466,0,587,698, 932,1175,1397,1175, - 698,0,880,1047, 698,0,523,440, 698,880,1047,1397, 1047,880,698,523, - 784,932,1175,1568, 1175,0,932,784, 587,0,466,392, 587,784,932,1175, - 523,0,622,784, 1047,0,784,622, 523,622,784,1047, 1245,1047,784,622, - 587,0,740,880, 1175,0,880,740, 587,740,880,1175, 1480,1175,880,740, - 784,0,587,466, 392,0,466,587, 784,0,932,1175, 784,587,466,392, + 698,0,0,880, 1047,0,0,880, 698,0,0,523, 440,0,523,698, + 587,0,0,698, 880,0,0,698, 587,0,0,440, 349,0,440,587, + 466,0,0,587, 698,0,0,587, 466,0,0,349, 233,0,349,466, + 523,0,0,659, 784,0,0,659, 523,0,0,440, 330,0,440,523, + 1397,0,1175,1047, 880,0,698,523, 440,523,698,880, 1047,880,698,523, + 1175,0,1047,880, 698,0,587,440, 349,440,587,698, 880,698,587,440, + 932,0,880,698, 587,0,466,349, 233,349,466,587, 698,587,466,349, + 1047,0,880,784, 659,523,440,349, 440,523,698,880, 1047,0,0,0, ], arp: [ - 196,233,294,233, 196,233,294,392, 294,392,294,233, 196,294,233,196, - 311,392,466,392, 311,392,466,622, 466,622,466,392, 311,466,392,311, - 233,294,349,294, 233,294,349,466, 349,466,349,294, 233,349,294,233, - 349,440,523,440, 349,440,523,698, 523,698,523,440, 349,523,440,349, - 196,233,294,392, 294,392,466,392, 294,233,196,233, 294,392,294,233, - 262,311,392,311, 262,311,392,523, 392,523,392,311, 262,392,311,262, - 294,370,440,370, 294,370,440,587, 440,587,440,370, 294,440,370,294, - 196,233,294,233, 196,294,233,196, 294,233,196,147, 196,233,294,0, + 175,220,262,220, 175,220,262,349, 262,220,175,220, 262,349,262,220, + 147,175,220,175, 147,175,220,294, 220,175,147,175, 220,294,220,175, + 233,294,349,294, 233,294,349,466, 349,294,233,294, 349,466,349,294, + 262,330,392,330, 262,330,392,523, 392,330,262,330, 392,523,392,330, + 175,262,349,523, 349,262,175,262, 349,523,698,523, 349,262,175,349, + 147,220,294,440, 294,220,147,220, 294,440,587,440, 294,220,147,294, + 233,349,466,698, 466,349,233,349, 466,698,932,698, 466,349,233,466, + 262,392,523,784, 523,392,262,392, 523,784,0,0, 262,0,0,0, ], - // i-VI-iv-V in G minor (melancholic beauty) - chords: [[196,233,294],[156,196,233],[131,156,196],[147,185,220],[196,233,294],[156,196,233],[147,185,220],[196,233,294]], + chords: [[175,220,262],[147,175,220],[233,294,349],[262,330,392],[175,220,262],[220,262,330],[233,294,349],[262,330,392]], drums: [ - 1,0,0,0, 2,0,0,3, 1,0,0,0, 2,0,0,0, 1,0,0,3, 2,0,0,0, 1,0,0,3, 2,0,0,0, - 1,0,0,3, 2,0,0,0, 1,0,3,0, 2,0,0,3, 1,0,0,3, 2,0,0,3, 1,0,3,0, 2,0,4,0, - 1,0,3,0, 2,0,0,3, 1,0,0,3, 2,0,0,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,4,0, - 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,3,3, 1,0,3,3, 2,0,3,3, 2,0,4,0, 1,0,0,0, + 1,0,0,0, 0,0,2,0, 0,0,0,0, 0,0,3,0, + 1,0,0,0, 0,0,2,0, 0,0,0,0, 0,0,3,0, + 1,0,0,3, 0,0,2,0, 0,3,0,0, 0,0,3,0, + 1,0,0,3, 0,0,2,0, 0,3,0,0, 2,0,3,4, + 1,0,3,0, 2,0,0,3, 1,0,3,0, 2,0,0,3, + 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,3,4, + 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,3,0, + 1,0,3,0, 2,3,3,0, 1,0,0,0, 0,0,0,0, ], } -// Track 7: "Block Height" — Em, 160 BPM — Rising momentum +// Track 7: "Shadow Assault" — 215 BPM, G minor, Ninja Gaiden action const track7: MusicTrack = { - name: 'Block Height', bpm: 160, + name: 'Shadow Assault', bpm: 215, synthStyle: 'ninja', bass: [ - 82,0,165,0, 82,0,123,0, 82,0,165,0, 82,123,165,0, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,0, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,0, - 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,0, - 82,0,165,0, 82,0,123,0, 82,0,165,123, 82,123,165,0, - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,165,220,0, - 123,0,247,0, 123,0,185,0, 123,0,247,185, 123,185,247,0, - 82,0,165,0, 82,0,123,0, 82,0,165,0, 82,0,0,0, + 98,0,196,98, 98,0,196,0, 98,0,196,98, 131,156,196,156, + 156,0,311,156, 156,0,311,0, 156,0,311,156, 196,233,311,233, + 131,0,262,131, 131,0,262,0, 131,0,262,131, 175,196,262,196, + 147,0,294,147, 147,0,294,0, 147,0,294,147, 185,220,294,220, + 98,196,98,196, 156,196,131,196, 98,196,98,196, 156,196,262,330, + 156,311,156,311, 196,311,233,311, 156,311,156,311, 196,311,392,466, + 131,262,131,262, 175,262,196,262, 131,262,131,262, 175,262,330,392, + 147,294,147,294, 185,294,220,294, 147,294,0,0, 147,0,0,0, ], lead: [ - 659,0,784,988, 659,0,494,392, 659,784,988,1319, 988,784,659,494, - 784,0,988,1175, 784,0,587,494, 784,988,1175,1568, 1175,988,784,587, - 587,0,740,880, 1175,0,880,740, 587,740,880,1175, 1480,1175,880,740, - 523,0,659,784, 1047,0,784,659, 523,659,784,1047, 1319,1047,784,659, - 659,784,988,1319, 1568,0,1319,988, 784,0,659,784, 988,1319,1568,1319, - 880,0,1047,1319, 880,0,659,523, 880,1047,1319,1760, 1319,1047,880,659, - 988,0,1245,1480, 988,0,740,622, 988,1245,1480,1976, 1480,1245,988,740, - 1319,0,988,784, 659,0,784,988, 1319,0,1568,1976, 1319,988,784,659, + 784,0,932,784, 0,784,932,1175, 1568,0,1175,932, 784,0,659,784, + 622,0,784,622, 0,622,784,988, 1175,0,988,784, 622,0,523,622, + 523,0,659,523, 0,523,659,784, 1047,0,784,659, 523,0,440,523, + 587,0,740,587, 0,587,740,880, 1175,0,880,740, 587,0,494,587, + 784,932,1175,1568, 1175,932,784,932, 1175,1568,1175,932, 784,659,784,932, + 622,784,988,1319, 988,784,622,784, 988,1319,988,784, 622,523,622,784, + 1047,1175,1568,1865, 1568,1175,1047,1175, 1568,1865,1568,1175, 1047,784,659,523, + 587,740,880,1175, 880,740,587,440, 587,0,784,0, 932,0,0,0, ], arp: [ - 330,392,494,392, 330,392,494,659, 494,659,494,392, 330,494,392,330, - 392,494,587,494, 392,494,587,784, 587,784,587,494, 392,587,494,392, - 294,370,440,370, 294,370,440,587, 440,587,440,370, 294,440,370,294, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 330,392,494,659, 494,659,784,659, 494,392,330,392, 494,659,494,392, - 440,523,659,523, 440,523,659,880, 659,880,659,523, 440,659,523,440, - 494,622,740,622, 494,622,740,988, 740,988,740,622, 494,740,622,494, - 330,392,494,392, 330,494,392,330, 494,392,330,247, 330,392,494,0, + 196,233,294,233, 196,233,294,392, 294,233,196,233, 294,392,294,233, + 311,392,466,392, 311,392,466,622, 466,392,311,392, 466,622,466,392, + 262,330,392,330, 262,330,392,523, 392,330,262,330, 392,523,392,330, + 294,370,440,370, 294,370,440,587, 440,370,294,370, 440,587,440,370, + 196,294,392,587, 392,294,196,294, 392,587,784,587, 392,294,196,392, + 311,466,622,932, 622,466,311,466, 622,932,1175,932, 622,466,311,622, + 262,392,523,784, 523,392,262,392, 523,784,1047,784, 523,392,262,523, + 294,440,587,880, 587,440,294,440, 587,0,0,0, 294,0,0,0, ], - // I-IV-vi-V in G major (uplifting pop) - chords: [[196,247,294],[131,165,196],[165,196,247],[147,185,220],[196,247,294],[131,165,196],[165,196,247],[196,247,294]], + chords: [[196,233,294],[311,392,466],[262,311,392],[294,370,440],[196,233,294],[233,294,349],[311,392,466],[294,370,440]], drums: [ - 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,4,0, - 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,3,3,3, 2,3,4,0, - 1,0,3,0, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,4,0, - 1,3,3,0, 2,3,3,3, 1,3,1,3, 2,3,3,3, 1,3,1,3, 2,3,1,3, 2,2,4,0, 1,0,0,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,4, + 1,3,0,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, + 1,3,0,3, 2,3,0,3, 1,3,0,3, 2,3,4,3, + 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,0,3,4, + 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,4,3, + 1,1,1,3, 2,1,1,3, 1,1,1,3, 2,1,4,1, + 1,1,1,1, 2,1,2,1, 2,2,2,1, 2,2,2,2, ], } -// Track 8: "Zero Conf" — Fm, 175 BPM — Urgent racing energy +// Track 8: "Emerald Rush" — 195 BPM, G major, Sonic-style funky speed const track8: MusicTrack = { - name: 'Zero Conf', bpm: 175, + name: 'Emerald Rush', bpm: 195, synthStyle: 'speed', bass: [ - 87,0,175,0, 87,0,131,0, 87,0,175,0, 87,131,175,131, - 139,0,277,0, 139,0,208,0, 139,0,277,0, 139,208,277,208, - 104,0,208,0, 104,0,156,0, 104,0,208,0, 104,156,208,156, - 156,0,311,0, 156,0,233,0, 156,0,311,0, 156,233,311,233, - 87,0,175,0, 87,0,131,0, 87,0,175,131, 87,131,175,131, - 117,0,233,0, 117,0,175,0, 117,0,233,0, 117,175,233,175, - 131,0,262,0, 131,0,196,0, 131,0,262,196, 131,196,262,196, - 87,0,175,0, 87,0,131,0, 87,0,175,0, 87,0,0,0, + 98,0,0,196, 0,0,98,0, 196,0,0,98, 0,196,0,98, + 82,0,0,165, 0,0,82,0, 165,0,0,82, 0,165,0,82, + 131,0,0,262, 0,0,131,0, 262,0,0,131, 0,262,0,131, + 73,0,0,147, 0,0,73,0, 147,0,0,73, 0,147,0,73, + 98,196,0,98, 196,0,98,196, 0,196,98,0, 196,98,196,262, + 82,165,0,82, 165,0,82,165, 0,165,82,0, 165,82,165,247, + 131,262,0,131, 262,0,131,262, 0,262,131,0, 262,131,262,330, + 147,294,0,147, 294,0,147,294, 147,0,0,0, 98,0,0,0, ], lead: [ - 698,0,831,1047, 698,0,523,415, 698,831,1047,1397, 1047,831,698,523, - 554,0,698,831, 1109,0,831,698, 554,698,831,1109, 1397,1109,831,698, - 831,0,1047,1245, 831,0,622,523, 831,1047,1245,1661, 1245,1047,831,622, - 622,0,784,932, 1245,0,932,784, 622,784,932,1245, 1568,1245,932,784, - 1047,831,698,523, 698,831,1047,1397, 1661,0,1397,1047, 831,698,523,415, - 932,0,1109,1397, 932,0,698,554, 932,1109,1397,1865, 1397,1109,932,698, - 523,0,659,784, 1047,0,784,659, 523,659,784,1047, 1319,1047,784,659, - 698,831,1047,1397, 1661,0,1397,1047, 831,0,698,831, 1047,698,415,349, + 784,0,880,1047, 1175,0,1047,880, 784,0,659,523, 659,784,880,1047, + 659,0,784,880, 1047,0,880,784, 659,0,523,440, 523,659,784,880, + 1047,0,1175,1319, 1568,0,1319,1175, 1047,0,880,784, 880,1047,1175,1319, + 587,0,659,784, 880,0,784,659, 587,0,523,440, 523,587,659,784, + 1175,1319,1568,1760, 1568,1319,1175,1047, 880,784,880,1047, 1175,1047,880,784, + 1047,1175,1319,1568, 1319,1175,1047,880, 784,659,784,880, 1047,880,784,659, + 1568,1760,2093,1760, 1568,1319,1175,1047, 880,784,659,523, 659,784,880,1047, + 880,784,659,587, 523,440,392,330, 392,440,523,659, 784,0,0,0, ], arp: [ - 349,415,523,415, 349,415,523,698, 523,698,523,415, 349,523,415,349, - 277,349,415,349, 277,349,415,554, 415,554,415,349, 277,415,349,277, - 415,523,622,523, 415,523,622,831, 622,831,622,523, 415,622,523,415, - 311,392,466,392, 311,392,466,622, 466,622,466,392, 311,466,392,311, - 349,415,523,698, 523,698,831,698, 523,415,349,415, 523,698,523,415, - 466,554,698,554, 466,554,698,932, 698,932,698,554, 466,698,554,466, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 349,415,523,415, 349,523,415,349, 523,415,349,262, 349,415,523,0, + 392,494,587,494, 392,494,587,784, 587,494,392,494, 587,784,587,494, + 330,392,494,392, 330,392,494,659, 494,392,330,392, 494,659,494,392, + 523,659,784,659, 523,659,784,1047, 784,659,523,659, 784,1047,784,659, + 294,370,440,370, 294,370,440,587, 440,370,294,370, 440,587,440,370, + 392,587,784,587, 392,494,587,784, 1047,784,587,494, 392,587,784,1047, + 330,494,659,494, 330,392,494,659, 880,659,494,392, 330,494,659,880, + 523,784,1047,784, 523,659,784,1047, 1319,1047,784,659, 523,784,1047,1319, + 294,440,587,440, 294,370,440,587, 784,587,0,0, 392,0,0,0, ], - // i-VII-VI-VII in F minor (oscillating tension) - chords: [[175,208,262],[156,196,233],[139,175,208],[156,196,233],[175,208,262],[139,175,208],[156,196,233],[175,208,262]], + chords: [[196,247,294],[165,196,247],[262,330,392],[147,175,220],[196,247,294],[123,147,185],[262,330,392],[147,175,220]], drums: [ - 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,3,0, 1,3,1,3, 2,0,3,0, 1,3,0,3, 2,0,4,0, - 1,3,1,3, 2,3,3,0, 1,3,0,3, 2,0,3,3, 1,3,1,3, 2,0,3,3, 1,3,3,3, 2,3,4,3, - 1,3,1,3, 2,3,3,0, 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,0,3,0, 1,3,1,3, 2,3,4,0, - 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,3,3, 1,1,1,1, 2,2,2,2, 2,2,4,2, 1,0,0,0, + 1,0,0,3, 2,0,0,3, 0,0,1,0, 2,0,3,0, + 1,0,0,3, 2,0,0,3, 0,3,1,0, 2,0,3,4, + 1,0,3,0, 2,0,0,3, 1,0,3,0, 2,0,3,0, + 1,0,3,0, 2,3,0,3, 1,0,3,0, 2,3,3,4, + 1,3,3,0, 2,0,3,3, 1,3,3,0, 2,0,3,4, + 1,3,3,0, 2,3,3,3, 1,3,3,0, 2,3,3,4, + 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,4,3, + 1,1,1,3, 2,1,2,1, 2,1,2,1, 2,2,2,2, ], } -// Track 9: "Mempool" — Dm, 152 BPM — Dark controlled chaos +// Track 9: "Alien Abyss" — 140 BPM, E minor, Metroid atmospheric tension const track9: MusicTrack = { - name: 'Mempool', bpm: 152, + name: 'Alien Abyss', bpm: 140, synthStyle: 'atmospheric', bass: [ - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,220,294,220, - 98,0,196,0, 98,0,147,0, 98,0,196,0, 98,147,196,147, - 117,0,233,0, 117,0,175,0, 117,0,233,0, 117,175,233,175, - 110,0,220,0, 110,0,165,0, 110,0,220,0, 110,165,220,165, - 147,0,294,0, 147,0,220,0, 147,0,294,220, 147,220,294,220, - 87,0,175,0, 87,0,131,0, 87,0,175,0, 87,131,175,131, - 131,0,262,0, 131,0,196,0, 131,0,262,196, 131,196,262,196, - 147,0,294,0, 147,0,220,0, 147,0,294,0, 147,0,0,0, + 82,0,0,0, 82,0,0,0, 165,0,0,0, 82,0,165,0, + 131,0,0,0, 131,0,0,0, 262,0,0,0, 131,0,262,0, + 110,0,0,0, 110,0,0,0, 220,0,0,0, 110,0,220,0, + 123,0,0,0, 123,0,0,0, 247,0,0,0, 123,0,247,0, + 82,0,165,0, 0,0,82,0, 165,0,0,0, 82,0,165,82, + 131,0,262,0, 0,0,131,0, 262,0,0,0, 131,0,262,131, + 110,0,220,0, 0,0,110,0, 220,0,0,0, 110,0,220,110, + 123,0,247,0, 123,0,0,0, 123,0,0,0, 82,0,0,0, ], lead: [ - 587,698,880,587, 698,880,1175,880, 698,587,440,587, 698,880,1175,1397, - 784,0,932,1175, 784,0,587,466, 784,932,1175,1568, 1175,932,784,587, - 932,0,1175,1397, 932,0,698,587, 466,587,698,932, 1175,1397,1865,1397, - 880,0,1109,1319, 880,0,659,554, 880,1109,1319,1760, 1319,1109,880,659, - 587,0,880,1175, 1397,0,1175,880, 698,587,698,880, 1175,1397,1760,1397, - 698,0,880,1047, 698,0,523,440, 698,880,1047,1397, 1760,1397,1047,880, - 523,659,784,1047, 1319,0,1047,784, 659,523,659,784, 1047,1319,1568,1319, - 1175,0,880,698, 587,0,698,880, 1175,0,1397,1760, 1175,880,698,587, + 494,0,0,659, 0,0,784,0, 0,0,659,0, 0,494,0,0, + 523,0,0,622, 0,0,784,0, 0,0,622,0, 0,523,0,0, + 440,0,0,523, 0,0,659,0, 0,0,523,0, 0,440,659,0, + 494,0,0,587, 0,0,740,0, 0,988,0,0, 740,0,587,0, + 659,0,784,0, 988,0,1319,0, 1568,0,1319,0, 988,784,659,494, + 523,0,659,0, 784,0,1047,0, 1319,0,1047,0, 784,659,523,392, + 440,0,523,0, 659,0,880,0, 1047,0,880,0, 659,523,440,330, + 494,0,587,0, 740,0,988,0, 740,0,587,0, 494,0,0,0, ], arp: [ - 294,349,440,349, 294,349,440,587, 440,587,440,349, 294,440,349,294, - 196,233,294,233, 196,233,294,392, 294,392,294,233, 196,294,233,196, - 233,294,349,294, 233,294,349,466, 349,466,349,294, 233,349,294,233, - 220,277,330,277, 220,277,330,440, 330,440,330,277, 220,330,277,220, - 294,349,440,587, 440,587,698,587, 440,349,294,349, 440,587,440,349, - 349,440,523,440, 349,440,523,698, 523,698,523,440, 349,523,440,349, - 262,330,392,330, 262,330,392,523, 392,523,392,330, 262,392,330,262, - 294,349,440,349, 294,440,349,294, 440,349,294,220, 294,349,440,0, + 330,392,494,0, 392,494,0,0, 330,392,494,0, 392,0,0,0, + 262,330,392,0, 330,392,0,0, 262,330,392,0, 330,0,0,0, + 220,262,330,0, 262,330,0,0, 220,262,330,0, 262,330,0,0, + 247,294,370,0, 294,370,494,0, 247,294,370,494, 294,370,494,0, + 330,494,659,494, 330,392,494,659, 784,659,494,392, 330,494,392,330, + 262,392,523,392, 262,330,392,523, 659,523,392,330, 262,392,330,262, + 220,330,440,330, 220,262,330,440, 523,440,330,262, 220,330,262,220, + 247,370,494,370, 247,294,370,494, 587,494,370,0, 0,0,0,0, ], - // i-iv-V-i → i-VI-VII-i in D minor (tension/release) - chords: [[147,175,220],[196,233,294],[220,277,330],[147,175,220],[147,175,220],[233,294,349],[262,330,392],[147,175,220]], + chords: [[165,196,247],[262,330,392],[220,262,330],[247,294,370],[165,196,247],[196,247,294],[220,262,330],[247,294,370]], drums: [ - 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,3,0, - 1,0,3,3, 2,0,3,0, 1,3,3,0, 2,0,3,3, 1,0,3,0, 2,3,3,0, 1,3,3,3, 2,3,4,3, - 1,0,3,0, 2,0,3,3, 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,0, 1,0,3,3, 2,0,4,0, - 1,3,3,0, 2,3,3,3, 1,3,1,3, 2,3,1,3, 1,3,1,3, 2,3,1,3, 2,2,4,0, 1,0,0,0, + 1,0,0,0, 0,0,0,0, 2,0,0,0, 0,0,0,0, + 1,0,0,0, 0,0,0,0, 2,0,0,0, 0,0,3,0, + 1,0,0,0, 0,0,3,0, 2,0,0,0, 0,0,3,0, + 1,0,0,3, 0,0,3,0, 2,0,3,0, 0,0,3,4, + 1,0,0,3, 2,0,3,0, 1,0,0,3, 2,0,3,0, + 1,0,3,0, 2,0,3,0, 1,0,3,0, 2,0,3,4, + 1,0,3,0, 2,0,3,3, 1,0,3,3, 2,0,3,4, + 1,0,3,0, 2,0,3,0, 1,0,0,0, 0,0,0,0, ], } // Track 10: "Halving Day" — Cm, 162 BPM — Triumphant climax const track10: MusicTrack = { - name: 'Halving Day', bpm: 162, + name: 'Halving Day', bpm: 162, synthStyle: 'heroic', bass: [ 131,0,262,0, 131,0,196,0, 131,0,262,0, 131,196,262,196, 156,0,311,0, 156,0,233,0, 156,0,311,0, 156,233,311,233,