Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Update as needed with: function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; var alreadySpawned = false; for (var i = 0; i < spawnedNotes.length; i++) { if (spawnedNotes[i] === noteKey) { alreadySpawned = true; break; } } if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) { var calculatedY = calculateNotePosition(noteData.time); // Calculate end note Y position for hold notes var endNoteY; if (noteData.type === 'hold' && noteData.duration > 0) { var endTime = noteData.time + noteData.duration; endNoteY = calculateNotePosition(endTime); } var calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); // Check for overlapping notes and adjust X position if needed var attempts = 0; var maxAttempts = 10; var noteRadius = 150; // Approximate radius to avoid overlap while (attempts < maxAttempts) { var hasOverlap = false; // Check against existing active notes for (var j = 0; j < notes.length; j++) { var existingNote = notes[j]; if (!existingNote || !existingNote.active) continue; var distance = Math.sqrt( Math.pow(calculatedX - existingNote.x, 2) + Math.pow(calculatedY - existingNote.y, 2) ); if (distance < noteRadius) { hasOverlap = true; break; } } if (!hasOverlap) { break; // Good position found } // Try a new X position within the same zone calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); attempts++; } var processedNoteData = { time: noteData.time, type: noteData.type, x: calculatedX, y: calculatedY, color: noteData.color, duration: noteData.duration }; var note = new Note(processedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: function calculateZoneX(zone, noteY, endNoteY) { var sideMargin = GAME_WIDTH * SAFETY_MARGIN_SIDES_PERCENT; var randomSpan = GAME_WIDTH / 2 - 2 * sideMargin; if (randomSpan < 0) { randomSpan = 0; } // Check if note or its end note is in the bottom 15% of the play area var isInBottomArea = noteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15; if (endNoteY !== undefined) { isInBottomArea = isInBottomArea || endNoteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15; } var xPosition; if (zone === 'left') { xPosition = sideMargin; if (isInBottomArea) { // In bottom area, favor the right side of the left zone (more central) // Use the right 60% of the left zone's available space var centralSpan = randomSpan * 0.6; var offset = randomSpan * 0.4; // Skip the leftmost 40% xPosition += offset + Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } else { xPosition = GAME_WIDTH / 2 + sideMargin; if (isInBottomArea) { // In bottom area, favor the left side of the right zone (more central) // Use the left 60% of the right zone's available space var centralSpan = randomSpan * 0.6; xPosition += Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } return xPosition; } ``` And youβll need to update the call to `calculateZoneX` in the `spawnNotesForCurrentTime` function: ```javascript function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; var alreadySpawned = false; for (var i = 0; i < spawnedNotes.length; i++) { if (spawnedNotes[i] === noteKey) { alreadySpawned = true; break; } } if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) { var calculatedY = calculateNotePosition(noteData.time); // Calculate end note Y position for hold notes var endNoteY; if (noteData.type === 'hold' && noteData.duration > 0) { var endTime = noteData.time + noteData.duration; endNoteY = calculateNotePosition(endTime); } var calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); var processedNoteData = { time: noteData.time, type: noteData.type, x: calculatedX, y: calculatedY, color: noteData.color, duration: noteData.duration }; var note = new Note(processedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: self.spawnIn = function () { self.isSpawning = true; tween(self, { alpha: 1, scaleX: 1.3, // Overshoot the target scale scaleY: 1.3 }, { duration: 200, // Much faster initial pop easing: tween.easeOut, onFinish: function onFinish() { // Then settle back to normal size with a bounce tween(self, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeElasticOut, // This creates the settling bounce onFinish: function onFinish() { self.isSpawning = false; self._initiateBeatPulseLoop(); } }); } }); }; βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
Update with: self.spawnIn = function () { self.isSpawning = true; tween(self, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800, // Increased duration for better elastic effect easing: tween.easeElasticOut, // Changed to elastic easing for bouncy appearance onFinish: function onFinish() { self.isSpawning = false; self._initiateBeatPulseLoop(); } }); }; ``` And modify the `showMissEffect` method to have a collapsing animation: ```javascript self.showMissEffect = function () { self._clearBeatPulseLoop(); LK.getSound('missSound').play(); // First change color to red tween(self.noteGraphics, { tint: 0xff0000 }, { duration: 150, onFinish: function onFinish() { // Then collapse inward with elastic ease-in for a crushing effect tween(self, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, // Slightly longer for dramatic effect easing: tween.easeElasticIn, // Elastic ease-in for collapsing effect onFinish: function onFinish() { self.active = false; self._clearBeatPulseLoop(); } }); } }); }; βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Instead of just using notecore asset for background particle. Add small random chance to use supernova core particle, stellar debris and nebula. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Move in the current direction - simple linear movement self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Reset particle if it goes off screen - improved distribution logic var margin = 100; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Use weighted probability to spawn on different edges // 60% opposite edge, 40% distributed across other edges var spawnChoice = Math.random(); if (spawnChoice < 0.6) { // 60% - Spawn on opposite edge (current behavior) if (Math.abs(currentDirection.x) > Math.abs(currentDirection.y)) { if (currentDirection.x > 0) { self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } else { self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } } else { if (currentDirection.y > 0) { self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; } else { self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; } } } else { // 40% - Spawn randomly on any edge for better distribution var edge = Math.floor(Math.random() * 4); switch(edge) { case 0: // Top edge self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; break; case 1: // Right edge self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; break; case 2: // Bottom edge self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; break; case 3: // Left edge self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; break; } } } }; βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: function changeParticleDirection() { if (isChangingDirection) return; // Calculate the turn direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); // Predict which corner will become bare and pre-fill it var turnDirection = angleChange; // Positive = turning counter-clockwise, Negative = turning clockwise // Determine which corner will be "left behind" by the turn var bareCornerX, bareCornerY; var currentDir = globalParticleDirection; var newDir = targetParticleDirection; // The bare corner is opposite to where particles are flowing after the turn if (newDir.x > 0 && newDir.y > 0) { // Flowing toward bottom-right, so top-left will be bare bareCornerX = 0; bareCornerY = 0; } else if (newDir.x < 0 && newDir.y > 0) { // Flowing toward bottom-left, so top-right will be bare bareCornerX = GAME_WIDTH; bareCornerY = 0; } else if (newDir.x < 0 && newDir.y < 0) { // Flowing toward top-left, so bottom-right will be bare bareCornerX = GAME_WIDTH; bareCornerY = GAME_HEIGHT; } else { // Flowing toward top-right, so bottom-left will be bare bareCornerX = 0; bareCornerY = GAME_HEIGHT; } // Spawn particles in the predicted bare corner area var cornerSize = Math.min(GAME_WIDTH, GAME_HEIGHT) * 0.3; // 30% of screen from corner for (var i = 0; i < 15; i++) { var extraParticle = new BackgroundParticle(); // Position in the bare corner area if (bareCornerX === 0) { extraParticle.x = Math.random() * cornerSize; } else { extraParticle.x = GAME_WIDTH - Math.random() * cornerSize; } if (bareCornerY === 0) { extraParticle.y = Math.random() * cornerSize; } else { extraParticle.y = GAME_HEIGHT - Math.random() * cornerSize; } backgroundParticles.push(extraParticle); game.addChildAt(extraParticle, 0); } isChangingDirection = true; directionChangeStartTime = Date.now(); } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
Code edit (3 edits merged)
Please save this source code
User prompt
When notes scale in use elastic easing for a rubber like spawning. Instead of just fading out when missed, reverse the spawn animation. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; // Distribute particles evenly across the screen self.x = Math.random() * GAME_WIDTH; self.y = Math.random() * GAME_HEIGHT; // Start particles throughout the screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Move in the current direction - simple linear movement self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Reset particle if it goes off screen - improved logic var margin = 100; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Respawn from the opposite side based on movement direction // and distribute evenly across that edge if (Math.abs(currentDirection.x) > Math.abs(currentDirection.y)) { // Primarily horizontal movement if (currentDirection.x > 0) { // Moving right, spawn from left edge self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left, spawn from right edge self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } } else { // Primarily vertical movement if (currentDirection.y > 0) { // Moving down, spawn from top edge self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; } else { // Moving up, spawn from bottom edge self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; } } } }; self.init(); return self; }); ``` Also, letβs update the setupGame function to ensure particles are properly distributed when starting a new game: ```javascript function setupGame(songName) { // ... existing code before particle setup ... // Reset particle speed for new game baseParticleSpeed = 1; isCountdownActive = false; countdownAcceleration = 1; // Reset particle direction to downward globalParticleDirection = { x: 0, y: 1 }; targetParticleDirection = { x: 0, y: 1 }; isChangingDirection = false; lastDirectionChangeTime = Date.now(); nextDirectionChangeDelay = 15000 + Math.random() * 15000; // Reinitialize particles for proper distribution if (backgroundParticles.length === 0) { for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) { var particle = new BackgroundParticle(); // Let init() handle the positioning backgroundParticles.push(particle); game.addChildAt(particle, 0); } } else { // Reinitialize existing particles for new distribution backgroundParticles.forEach(function (p) { p.init(); }); } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: // Add these global variables (replace the previous ones) var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var baseParticleSpeed = 1; // Base speed multiplier var countdownAcceleration = 1; // Separate from base speed var isCountdownActive = false; // Fixed BackgroundParticle class var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.initialScale = 1; self.baseAlpha = 0.1; self.sineAmplitude = 0; self.sineFrequency = 0; self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Store previous position for sine calculation var prevX = self.x; var prevY = self.y; // Move in the current direction self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Apply sine wave effect perpendicular to movement direction // But keep it subtle and relative to distance traveled var distanceThisFrame = totalSpeed; var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * (self.x + self.y)); // Apply sine offset perpendicular to movement direction var perpX = -currentDirection.y; var perpY = currentDirection.x; self.x += perpX * sineOffset * 0.01; // Much smaller sine effect self.y += perpY * sineOffset * 0.01; // Reset particle if it goes off screen - improved logic var margin = 100; var shouldReset = false; var newX, newY; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Respawn from the opposite side based on movement direction if (currentDirection.x > 0.1) { // Moving right, spawn from left newX = -margin + Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.x < -0.1) { // Moving left, spawn from right newX = GAME_WIDTH + margin - Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.y > 0.1) { // Moving down, spawn from top newX = Math.random() * GAME_WIDTH; newY = -margin + Math.random() * 50; } else { // Moving up, spawn from bottom newX = Math.random() * GAME_WIDTH; newY = GAME_HEIGHT + margin - Math.random() * 50; } self.x = newX; self.y = newY; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } // Modified startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; countdownAcceleration = 1; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } // Keep the accelerated speed when game starts baseParticleSpeed = 5; // Maintain 5x speed isCountdownActive = false; countdownAcceleration = 1; startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownAcceleration = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Modified setupGame function - add this line to reset particles when starting a new game function setupGame(songName) { // ... existing setupGame code ... // Reset particle speed for new game baseParticleSpeed = 1; isCountdownActive = false; countdownAcceleration = 1; βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: // Modify the startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } isCountdownActive = false; countdownSpeedMultiplier = 1; // Reset to normal speed startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownSpeedMultiplier = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Add to the game.update function (add this before the existing update code) game.update = function () { // Update background particles with new space travel effects backgroundParticles.forEach(function (particle) { particle.updateParticle(); }); // Handle direction changes during gameplay if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) { var currentTime = Date.now(); if (currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); } } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.speed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self.initialX = 0; self.sineAmplitude = 0; self.sineFrequency = 0; self.distanceTraveled = 0; // For sine wave calculation self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.speed = self.baseSpeed; self.x = Math.random() * GAME_WIDTH; self.initialX = self.x; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.y = -Math.random() * 100 - self.particleGraphics.height * scale; // Start above screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self.distanceTraveled = 0; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Apply current speed multiplier (for countdown effect) var currentSpeed = self.baseSpeed * countdownSpeedMultiplier; // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Move in the current direction self.x += currentDirection.x * currentSpeed; self.y += currentDirection.y * currentSpeed; // Update distance traveled for sine wave self.distanceTraveled += currentSpeed; // Apply sine wave perpendicular to movement direction var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * self.distanceTraveled); // Calculate perpendicular direction for sine wave var perpX = -currentDirection.y; // Perpendicular to direction var perpY = currentDirection.x; // Apply sine offset var sineX = self.initialX + perpX * sineOffset; var sineY = self.y + perpY * sineOffset; // Only apply sine to X if moving primarily vertically, and vice versa if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) { self.x = sineX; } else { self.y = sineY; } // Reset particle if it goes off screen var margin = self.particleGraphics.height * self.initialScale; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { self.init(); // Start from appropriate edge based on current direction if (currentDirection.y > 0.5) { // Moving down self.y = -Math.random() * 100 - margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.y < -0.5) { // Moving up self.y = GAME_HEIGHT + Math.random() * 100 + margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.x > 0.5) { // Moving right self.x = -Math.random() * 100 - margin; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left self.x = GAME_WIDTH + Math.random() * 100 + margin; self.y = Math.random() * GAME_HEIGHT; } self.initialX = self.x; self.distanceTraveled = 0; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Implement a new background particles system using as needed: ```javascript // Add these global variables near the top with other game variables var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var countdownSpeedMultiplier = 1; var isCountdownActive = false; // Modified BackgroundParticle class var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.speed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self.initialX = 0; self.sineAmplitude = 0; self.sineFrequency = 0; self.distanceTraveled = 0; // For sine wave calculation self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.speed = self.baseSpeed; self.x = Math.random() * GAME_WIDTH; self.initialX = self.x; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.y = -Math.random() * 100 - self.particleGraphics.height * scale; // Start above screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self.distanceTraveled = 0; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Apply current speed multiplier (for countdown effect) var currentSpeed = self.baseSpeed * countdownSpeedMultiplier; // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Move in the current direction self.x += currentDirection.x * currentSpeed; self.y += currentDirection.y * currentSpeed; // Update distance traveled for sine wave self.distanceTraveled += currentSpeed; // Apply sine wave perpendicular to movement direction var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * self.distanceTraveled); // Calculate perpendicular direction for sine wave var perpX = -currentDirection.y; // Perpendicular to direction var perpY = currentDirection.x; // Apply sine offset var sineX = self.initialX + perpX * sineOffset; var sineY = self.y + perpY * sineOffset; // Only apply sine to X if moving primarily vertically, and vice versa if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) { self.x = sineX; } else { self.y = sineY; } // Reset particle if it goes off screen var margin = self.particleGraphics.height * self.initialScale; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { self.init(); // Start from appropriate edge based on current direction if (currentDirection.y > 0.5) { // Moving down self.y = -Math.random() * 100 - margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.y < -0.5) { // Moving up self.y = GAME_HEIGHT + Math.random() * 100 + margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.x > 0.5) { // Moving right self.x = -Math.random() * 100 - margin; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left self.x = GAME_WIDTH + Math.random() * 100 + margin; self.y = Math.random() * GAME_HEIGHT; } self.initialX = self.x; self.distanceTraveled = 0; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } ``` Now I need to modify the countdown function and game update loop. Here are the changes: ```javascript // Modify the startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } isCountdownActive = false; countdownSpeedMultiplier = 1; // Reset to normal speed startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownSpeedMultiplier = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Add to the game.update function (add this before the existing update code) game.update = function () { // Update background particles with new space travel effects backgroundParticles.forEach(function (particle) { particle.updateParticle(); }); // Handle direction changes during gameplay if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) { var currentTime = Date.now(); if (currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); } } // ... rest of existing update function remains the same βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
// Add these global variables near the top with other game variables var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var countdownSpeedMultiplier = 1; var isCountdownActive = false;
Code edit (2 edits merged)
Please save this source code
User prompt
Allow misses for A grade but keep hit rate.
Code edit (1 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BackgroundParticle = Container.expand(function () {
var self = Container.call(this);
self.particleGraphics = null;
self.baseSpeed = 0;
self.initialScale = 1;
self.baseAlpha = 0.1;
self._startTwinkleAnimation = function () {
tween.stop(self, {
alpha: true
});
var currentAlpha = self.alpha;
var pulseDelta = self.baseAlpha * 0.4;
var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta);
var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta);
if (targetMaxAlpha - targetMinAlpha < 0.05) {
if (self.baseAlpha < 0.15) {
targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1);
} else if (self.baseAlpha > 0.85) {
targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1);
}
if (targetMaxAlpha - targetMinAlpha < 0.05) {
return;
}
}
var nextTargetAlpha;
if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) {
nextTargetAlpha = targetMinAlpha;
} else {
nextTargetAlpha = targetMaxAlpha;
}
var pulseDuration = 300 + Math.random() * 400;
tween(self, {
alpha: nextTargetAlpha
}, {
duration: pulseDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self && self._startTwinkleAnimation) {
self._startTwinkleAnimation();
}
}
});
};
self.init = function () {
tween.stop(self, {
alpha: true
});
var scale = 0.312 + Math.random() * 1.248;
self.initialScale = scale;
self.scale.set(scale);
var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6];
var color = colors[Math.floor(Math.random() * colors.length)];
if (self.particleGraphics) {
self.particleGraphics.destroy();
}
self.particleGraphics = self.attachAsset('starParticle', {
anchorX: 0.5,
anchorY: 0.5
});
self.particleGraphics.tint = color;
self.baseSpeed = (0.5 + Math.random() * 1.5) * scale;
// Distribute particles evenly across the screen
self.x = Math.random() * GAME_WIDTH;
self.y = Math.random() * GAME_HEIGHT; // Start particles throughout the screen
self.baseAlpha = 0.1 + Math.random() * 0.7;
self.alpha = self.baseAlpha;
self._startTwinkleAnimation();
};
self.updateParticle = function () {
// Get current direction (smoothly interpolated if changing)
var currentDirection = getCurrentParticleDirection();
// Calculate total speed (base * game speed * countdown acceleration)
var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1);
// Move in the current direction - simple linear movement
self.x += currentDirection.x * totalSpeed;
self.y += currentDirection.y * totalSpeed;
// Reset particle if it goes off screen - improved logic
var margin = 100;
if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) {
// Respawn from the opposite side based on movement direction
// and distribute evenly across that edge
if (Math.abs(currentDirection.x) > Math.abs(currentDirection.y)) {
// Primarily horizontal movement
if (currentDirection.x > 0) {
// Moving right, spawn from left edge
self.x = -margin + Math.random() * 50;
self.y = Math.random() * GAME_HEIGHT;
} else {
// Moving left, spawn from right edge
self.x = GAME_WIDTH + margin - Math.random() * 50;
self.y = Math.random() * GAME_HEIGHT;
}
} else {
// Primarily vertical movement
if (currentDirection.y > 0) {
// Moving down, spawn from top edge
self.x = Math.random() * GAME_WIDTH;
self.y = -margin + Math.random() * 50;
} else {
// Moving up, spawn from bottom edge
self.x = Math.random() * GAME_WIDTH;
self.y = GAME_HEIGHT + margin - Math.random() * 50;
}
}
}
};
self.init();
return self;
});
var Note = Container.expand(function (noteData, spawnTime) {
var self = Container.call(this);
self.noteData = noteData;
self.type = noteData.type;
self.targetX = noteData.x;
self.targetY = noteData.y;
self.hitTime = noteData.time;
self.duration = noteData.duration || 0;
self.spawnTime = spawnTime;
self.zone = noteData.x < GAME_WIDTH / 2 ? 'left' : 'right';
if (noteData.color && noteData.color === SYNC_NOTE_COLOR) {
if (self.type === 'tap') {
self.color = COLOR_SYNC_TAP;
} else if (self.type === 'hold') {
self.color = COLOR_SYNC_HOLD;
} else {
self.color = self.zone === 'left' ? COLOR_LEFT_ZONE : COLOR_RIGHT_ZONE;
}
} else {
self.color = self.zone === 'left' ? COLOR_LEFT_ZONE : COLOR_RIGHT_ZONE;
}
self.active = true;
self.isHit = false;
self.isMissed = false;
self.isSpawning = true;
self.isHolding = false;
self.holdSuccessfullyCompleted = false;
self.holdParticles = [];
self.lastParticleEmissionTime = 0;
self.PARTICLE_EMISSION_INTERVAL = 50;
self.holdStarted = false;
self.beatPulseInterval = null;
var assetName = self.type === 'hold' ? 'holdNoteCore' : 'noteCore';
self.noteGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.noteGraphics.tint = self.color;
self._startBeatPulseAnimation = function () {
if (!self || !self.active || self.isHit || self.isMissed || self.isSpawning) {
tween.stop(self, {
scaleX: true,
scaleY: true
});
return;
}
tween.stop(self, {
scaleX: true,
scaleY: true
});
tween(self, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: BEAT_DURATION_MS * 0.2,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: BEAT_DURATION_MS * 0.2,
easing: tween.easeIn
});
}
}
});
};
self._clearBeatPulseLoop = function () {
if (self.beatPulseInterval) {
LK.clearInterval(self.beatPulseInterval);
self.beatPulseInterval = null;
}
tween.stop(self, {
scaleX: true,
scaleY: true
});
};
self._initiateBeatPulseLoop = function () {
self._clearBeatPulseLoop();
if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) {
self._startBeatPulseAnimation();
self.beatPulseInterval = LK.setInterval(function () {
if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) {
self._startBeatPulseAnimation();
} else {
self._clearBeatPulseLoop();
}
}, BEAT_DURATION_MS);
}
};
self.x = self.targetX;
self.y = self.targetY;
self.alpha = 0;
self.scaleX = 0.1;
self.scaleY = 0.1;
self.createHoldTrail = function () {
var cycleTime = self.hitTime % SCANNER_CYCLE_DURATION;
var scannerMovingUp = cycleTime < SCANNER_HALF_CYCLE;
var direction = scannerMovingUp ? -1 : 1;
var originalRequestedDuration = self.duration;
var maxPossibleTrailPixelLength = originalRequestedDuration / SCANNER_HALF_CYCLE * PLAY_AREA_HEIGHT;
var actualTrailPixelLength = maxPossibleTrailPixelLength;
if (direction === -1) {
if (self.y - actualTrailPixelLength < SCANNER_Y_MIN) {
actualTrailPixelLength = self.y - SCANNER_Y_MIN;
}
} else {
if (self.y + actualTrailPixelLength > SCANNER_Y_MAX) {
actualTrailPixelLength = SCANNER_Y_MAX - self.y;
}
}
actualTrailPixelLength = Math.max(0, actualTrailPixelLength);
if (actualTrailPixelLength < maxPossibleTrailPixelLength) {
if (PLAY_AREA_HEIGHT > 0 && SCANNER_HALF_CYCLE > 0) {
self.duration = actualTrailPixelLength / PLAY_AREA_HEIGHT * SCANNER_HALF_CYCLE;
} else if (actualTrailPixelLength === 0) {
self.duration = 0;
}
}
self.duration = Math.max(0, self.duration);
// Create trail line first so it's below everything else
var trailLine = self.attachAsset('holdTrail', {
anchorX: 0.5,
anchorY: 0
});
trailLine.tint = self.color;
trailLine.alpha = 0.6;
trailLine.height = actualTrailPixelLength;
if (direction === -1) {
trailLine.y = -actualTrailPixelLength;
} else {
trailLine.y = 0;
}
self.holdTrails.push(trailLine);
// Move the trail to index 0 so it's below the note graphics
self.setChildIndex(trailLine, 0);
self.endNote = self.attachAsset('noteCore', {
anchorX: 0.5,
anchorY: 0.5
});
self.endNote.tint = self.color;
self.endNote.alpha = 0.8;
self.endNote.scaleX = 0.7;
self.endNote.scaleY = 0.7;
self.endNote.y = actualTrailPixelLength * direction;
self.endGlow = self.attachAsset('glowRing', {
anchorX: 0.5,
anchorY: 0.5
});
self.endGlow.tint = self.color;
self.endGlow.alpha = 0.3;
self.endGlow.scaleX = 0.5;
self.endGlow.scaleY = 0.5;
self.endGlow.y = actualTrailPixelLength * direction;
};
self.holdTrails = [];
if (self.type === 'hold' && self.duration > 0) {
self.createHoldTrail();
}
self.spawnIn = function () {
self.isSpawning = true;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isSpawning = false;
self._initiateBeatPulseLoop();
}
});
};
self.showHitEffect = function () {
self._clearBeatPulseLoop();
LK.getSound('hitSound').play();
self.explodeIntoParticles();
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.active = false;
self._clearBeatPulseLoop();
}
});
};
self.startHoldEffect = function () {
self._clearBeatPulseLoop();
LK.getSound('hitSound').play();
tween(self.noteGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self.noteGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
};
self.showMissEffect = function () {
self._clearBeatPulseLoop();
LK.getSound('missSound').play();
tween(self.noteGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
alpha: 0.3
}, {
duration: 300,
onFinish: function onFinish() {
self.active = false;
self._clearBeatPulseLoop();
}
});
}
});
};
self.explodeIntoParticles = function () {
var noteScale = 1.2; // Base scale relative to noteCore (300px)
// Stage 1: Initial Flash (0-200ms)
var flash = game.attachAsset('supernovaFlash', {
anchorX: 0.5,
anchorY: 0.5
});
flash.tint = 0xFFFFFF;
flash.x = self.x;
flash.y = self.y;
flash.alpha = 0;
flash.scale.set(0.1 * noteScale);
tween(flash, {
alpha: 1,
scaleX: 2.8 * noteScale,
scaleY: 2.8 * noteScale
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(flash, {
alpha: 0
}, {
duration: 350,
onFinish: function onFinish() {
if (flash && flash.destroy) {
flash.destroy();
}
}
});
}
});
// Stage 2: Shockwave (100-600ms)
LK.setTimeout(function () {
var shockwave = game.attachAsset('supernovaShockwave', {
anchorX: 0.5,
anchorY: 0.5
});
shockwave.tint = 0x00FFFF;
shockwave.x = self.x;
shockwave.y = self.y;
shockwave.alpha = 1;
shockwave.scale.set(0.2 * noteScale);
tween(shockwave, {
scaleX: 5.0 * noteScale,
scaleY: 5.0 * noteScale,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (shockwave && shockwave.destroy) {
shockwave.destroy();
}
}
});
}, 100);
// Stage 3: Core Particles (50-800ms)
var coreParticleCount = 15;
for (var i = 0; i < coreParticleCount; i++) {
(function (index) {
LK.setTimeout(function () {
var coreAsset = Math.random() < 0.5 ? 'supernovaCoreParticle' : 'supernovaCoreParticle2';
var particle = game.attachAsset(coreAsset, {
anchorX: 0.5,
anchorY: 0.5
});
particle.tint = Math.random() < 0.5 ? 0xFFFFFF : 0xFFFF88;
particle.x = self.x;
particle.y = self.y;
var angle = index / coreParticleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var speed = 200 + Math.random() * 150;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
var finalScale = (0.5 + Math.random() * 0.4) * noteScale;
particle.scale.set(0.2 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale
}, {
duration: 900,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 50 + Math.random() * 100);
})(i);
}
// Stage 4: Nebula Trails (200-1200ms)
var nebulaCount = 20;
for (var j = 0; j < nebulaCount; j++) {
(function (index) {
LK.setTimeout(function () {
var nebulaAssets = ['supernovaNebulaTrail', 'supernovaNebulaTrail2', 'supernovaNebulaTrail3'];
var nebulaAsset = nebulaAssets[Math.floor(Math.random() * nebulaAssets.length)];
var particle = game.attachAsset(nebulaAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var colors = [0x8844FF, 0xFF44AA, 0x4488FF, 0xAA44FF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = self.x + (Math.random() - 0.5) * 40;
particle.y = self.y + (Math.random() - 0.5) * 40;
particle.alpha = 0.8 + Math.random() * 0.3;
var angle = Math.random() * Math.PI * 2;
var speed = 80 + Math.random() * 120;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
var finalScale = (0.4 + Math.random() * 0.3) * noteScale;
particle.scale.set(0.2 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale,
rotation: Math.random() * Math.PI * 2
}, {
duration: 1300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 200 + Math.random() * 200);
})(j);
}
// Stage 5: Stellar Debris (150-1000ms)
var debrisCount = 12;
for (var k = 0; k < debrisCount; k++) {
(function (index) {
LK.setTimeout(function () {
var debrisAsset = Math.random() < 0.5 ? 'supernovaStellarDebris' : 'supernovaStellarDebris2';
var particle = game.attachAsset(debrisAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var colors = [0xFF4400, 0xFF6600, 0xFFAA00, 0xFFFFFF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = self.x;
particle.y = self.y;
var angle = Math.random() * Math.PI * 2;
var speed = 120 + Math.random() * 100;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
var finalScale = (0.25 + Math.random() * 0.25) * noteScale;
particle.scale.set(0.10 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale,
rotation: Math.random() * Math.PI * 4 // Multiple rotations
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 150 + Math.random() * 150);
})(k);
}
// Stage 6: Energy Sparks (100-700ms)
var sparkCount = 25;
for (var l = 0; l < sparkCount; l++) {
(function (index) {
LK.setTimeout(function () {
var sparkAsset = Math.random() < 0.5 ? 'supernovaEnergySpark' : 'supernovaEnergySpark2';
var particle = game.attachAsset(sparkAsset, {
anchorX: 0.5,
anchorY: 0.5
});
particle.tint = Math.random() < 0.5 ? 0x00FFFF : 0x0088FF;
particle.x = self.x + (Math.random() - 0.5) * 20;
particle.y = self.y + (Math.random() - 0.5) * 20;
var angle = Math.random() * Math.PI * 2;
var speed = 180 + Math.random() * 220;
var targetX = self.x + Math.cos(angle) * speed;
var targetY = self.y + Math.sin(angle) * speed;
var finalScale = (0.2 + Math.random() * 0.2) * noteScale;
particle.scale.set(0.1 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale
}, {
duration: 900,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 100 + Math.random() * 100);
})(l);
}
// Stage 7: Afterglow (300-1500ms)
LK.setTimeout(function () {
var afterglow = game.attachAsset('supernovaAfterglow', {
anchorX: 0.5,
anchorY: 0.5
});
afterglow.tint = 0xFF6600;
afterglow.x = self.x;
afterglow.y = self.y;
afterglow.alpha = 0.4;
afterglow.scale.set(1.2 * noteScale);
tween(afterglow, {
alpha: 0,
scaleX: 3.0 * noteScale,
scaleY: 3.0 * noteScale
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (afterglow && afterglow.destroy) {
afterglow.destroy();
}
}
});
}, 400);
};
self.explodeEndNoteParticles = function () {
if (!self.endNote) {
return;
}
var noteScale = 1.2; // Same scale as main note
var endNoteGlobalX = self.x;
var endNoteGlobalY = self.y + self.endNote.y;
// Stage 1: Initial Flash (0-200ms)
var flash = game.attachAsset('supernovaFlash', {
anchorX: 0.5,
anchorY: 0.5
});
flash.tint = 0xFFFFFF;
flash.x = endNoteGlobalX;
flash.y = endNoteGlobalY;
flash.alpha = 0;
flash.scale.set(0.1 * noteScale);
tween(flash, {
alpha: 1,
scaleX: 2.5 * noteScale,
scaleY: 2.5 * noteScale
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(flash, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
if (flash && flash.destroy) {
flash.destroy();
}
}
});
}
});
// Stage 2: Shockwave (100-600ms)
LK.setTimeout(function () {
var shockwave = game.attachAsset('supernovaShockwave', {
anchorX: 0.5,
anchorY: 0.5
});
shockwave.tint = 0x00FFFF;
shockwave.x = endNoteGlobalX;
shockwave.y = endNoteGlobalY;
shockwave.alpha = 0.9;
shockwave.scale.set(0.2 * noteScale);
tween(shockwave, {
scaleX: 5.0 * noteScale,
scaleY: 5.0 * noteScale,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (shockwave && shockwave.destroy) {
shockwave.destroy();
}
}
});
}, 100);
// Stage 3: Core Particles (50-800ms)
var coreParticleCount = 15;
for (var i = 0; i < coreParticleCount; i++) {
(function (index) {
LK.setTimeout(function () {
var coreAsset = Math.random() < 0.5 ? 'supernovaCoreParticle' : 'supernovaCoreParticle2';
var particle = game.attachAsset(coreAsset, {
anchorX: 0.5,
anchorY: 0.5
});
particle.tint = Math.random() < 0.5 ? 0xFFFFFF : 0xFFFF88;
particle.x = endNoteGlobalX;
particle.y = endNoteGlobalY;
var angle = index / coreParticleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var speed = 200 + Math.random() * 150;
var targetX = endNoteGlobalX + Math.cos(angle) * speed;
var targetY = endNoteGlobalY + Math.sin(angle) * speed;
var finalScale = (0.3 + Math.random() * 0.4) * noteScale;
particle.scale.set(0.1 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale
}, {
duration: 750,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 50 + Math.random() * 100);
})(i);
}
// Stage 4: Nebula Trails (200-1200ms)
var nebulaCount = 20;
for (var j = 0; j < nebulaCount; j++) {
(function (index) {
LK.setTimeout(function () {
var nebulaAssets = ['supernovaNebulaTrail', 'supernovaNebulaTrail2', 'supernovaNebulaTrail3'];
var nebulaAsset = nebulaAssets[Math.floor(Math.random() * nebulaAssets.length)];
var particle = game.attachAsset(nebulaAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var colors = [0x8844FF, 0xFF44AA, 0x4488FF, 0xAA44FF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = endNoteGlobalX + (Math.random() - 0.5) * 40;
particle.y = endNoteGlobalY + (Math.random() - 0.5) * 40;
particle.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.random() * Math.PI * 2;
var speed = 80 + Math.random() * 120;
var targetX = endNoteGlobalX + Math.cos(angle) * speed;
var targetY = endNoteGlobalY + Math.sin(angle) * speed;
var finalScale = (0.2 + Math.random() * 0.3) * noteScale;
particle.scale.set(0.1 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale,
rotation: Math.random() * Math.PI * 2
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 200 + Math.random() * 200);
})(j);
}
// Stage 5: Stellar Debris (150-1000ms)
var debrisCount = 12;
for (var k = 0; k < debrisCount; k++) {
(function (index) {
LK.setTimeout(function () {
var debrisAsset = Math.random() < 0.5 ? 'supernovaStellarDebris' : 'supernovaStellarDebris2';
var particle = game.attachAsset(debrisAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var colors = [0xFF4400, 0xFF6600, 0xFFAA00, 0xFFFFFF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = endNoteGlobalX;
particle.y = endNoteGlobalY;
var angle = Math.random() * Math.PI * 2;
var speed = 120 + Math.random() * 100;
var targetX = endNoteGlobalX + Math.cos(angle) * speed;
var targetY = endNoteGlobalY + Math.sin(angle) * speed;
var finalScale = (0.15 + Math.random() * 0.25) * noteScale;
particle.scale.set(0.05 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale,
rotation: Math.random() * Math.PI * 4
}, {
duration: 850,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 150 + Math.random() * 150);
})(k);
}
// Stage 6: Energy Sparks (100-700ms)
var sparkCount = 25;
for (var l = 0; l < sparkCount; l++) {
(function (index) {
LK.setTimeout(function () {
var sparkAsset = Math.random() < 0.5 ? 'supernovaEnergySpark' : 'supernovaEnergySpark2';
var particle = game.attachAsset(sparkAsset, {
anchorX: 0.5,
anchorY: 0.5
});
particle.tint = Math.random() < 0.5 ? 0x00FFFF : 0x0088FF;
particle.x = endNoteGlobalX + (Math.random() - 0.5) * 20;
particle.y = endNoteGlobalY + (Math.random() - 0.5) * 20;
var angle = Math.random() * Math.PI * 2;
var speed = 180 + Math.random() * 220;
var targetX = endNoteGlobalX + Math.cos(angle) * speed;
var targetY = endNoteGlobalY + Math.sin(angle) * speed;
var finalScale = (0.1 + Math.random() * 0.2) * noteScale;
particle.scale.set(0.05 * noteScale);
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: finalScale,
scaleY: finalScale
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
}
});
}, 100 + Math.random() * 100);
})(l);
}
// Stage 7: Afterglow (300-1500ms)
LK.setTimeout(function () {
var afterglow = game.attachAsset('supernovaAfterglow', {
anchorX: 0.5,
anchorY: 0.5
});
afterglow.tint = 0xFF6600;
afterglow.x = endNoteGlobalX;
afterglow.y = endNoteGlobalY;
afterglow.alpha = 0.4;
afterglow.scale.set(1.2 * noteScale);
tween(afterglow, {
alpha: 0,
scaleX: 2.0 * noteScale,
scaleY: 2.0 * noteScale
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (afterglow && afterglow.destroy) {
afterglow.destroy();
}
}
});
}, 300);
};
self.updateHoldEffects = function (scannerCurrentY) {
if (self.type === 'hold' && self.isHolding && self.active && !self.holdSuccessfullyCompleted && !self.isMissed) {
var currentTime = Date.now();
if (currentTime - self.lastParticleEmissionTime > self.PARTICLE_EMISSION_INTERVAL) {
self.lastParticleEmissionTime = currentTime;
var trailStartY = self.y;
var trailEndY = self.y + (self.endNote ? self.endNote.y : 0);
var scannerIsOverTrail = self.endNote && scannerCurrentY >= Math.min(trailStartY, trailEndY) && scannerCurrentY <= Math.max(trailStartY, trailEndY);
if (scannerIsOverTrail) {
var particle = game.attachAsset('holdConnectionParticle', {
anchorX: 0.5,
anchorY: 0.5
});
particle.x = self.x;
particle.y = scannerCurrentY;
particle.tint = self.color;
particle.alpha = 0.8;
self.holdParticles.push(particle);
tween(particle, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.destroy) {
particle.destroy();
}
var index = self.holdParticles.indexOf(particle);
if (index > -1) {
self.holdParticles.splice(index, 1);
}
}
});
}
}
}
};
self.cleanupHoldVisuals = function () {
self.holdParticles.forEach(function (p) {
if (p && p.destroy) {
p.destroy();
}
});
self.holdParticles = [];
if (self.endNote && self.endNote.destroy) {
self.endNote.destroy();
}
if (self.endGlow && self.endGlow.destroy) {
self.endGlow.destroy();
}
self.endNote = null;
self.endGlow = null;
self.holdTrails.forEach(function (trail) {
if (trail && trail.destroy) {
trail.destroy();
}
});
self.holdTrails = [];
};
self.completeHold = function () {
if (self.holdSuccessfullyCompleted || self.isMissed) {
return;
}
self._clearBeatPulseLoop();
self.holdSuccessfullyCompleted = true;
self.isHolding = false;
self.explodeIntoParticles();
if (self.type === 'hold') {
self.explodeEndNoteParticles();
}
self.cleanupHoldVisuals();
tween(self, {
alpha: 0
}, {
duration: 100,
delay: 100,
onFinish: function onFinish() {
self.active = false;
self._clearBeatPulseLoop();
}
});
};
self.failHold = function () {
if (self.isMissed || self.holdSuccessfullyCompleted) {
return;
}
self._clearBeatPulseLoop();
self.isMissed = true;
self.isHolding = false;
self.showMissEffect();
self.cleanupHoldVisuals();
};
return self;
});
var Scanner = Container.expand(function () {
var self = Container.call(this);
self.glow = self.attachAsset('scannerGlow', {
anchorX: 0,
anchorY: 0.5
});
self.glow.alpha = 0.2;
self.line = self.attachAsset('scannerLine', {
anchorX: 0,
anchorY: 0.5
});
self.x = 0;
self.isMovingUp = true;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Add these global variables (replace the previous ones)
var globalParticleDirection = {
x: 0,
y: 1
}; // Start moving downward
var targetParticleDirection = {
x: 0,
y: 1
};
var directionChangeStartTime = 0;
var directionChangeDuration = 1000; // 1 second
var isChangingDirection = false;
var lastDirectionChangeTime = 0;
var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds
var baseParticleSpeed = 1; // Base speed multiplier
var countdownAcceleration = 1; // Separate from base speed
var isCountdownActive = false;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var SAFETY_MARGIN_TOP_PERCENT = 0.05;
var SAFETY_MARGIN_BOTTOM_PERCENT = 0.15;
var SAFETY_MARGIN_SIDES_PERCENT = 0.10;
var BPM = 120;
var BEAT_DURATION_MS = 60 / BPM * 1000;
var SCANNER_Y_MIN = GAME_HEIGHT * SAFETY_MARGIN_TOP_PERCENT;
var SCANNER_Y_MAX = GAME_HEIGHT * (1 - SAFETY_MARGIN_BOTTOM_PERCENT);
var PLAY_AREA_HEIGHT = SCANNER_Y_MAX - SCANNER_Y_MIN;
var NOTE_SPAWN_AHEAD_MS = 800;
var PERFECT_HIT_RATIO = 0.4;
var POINTS_PERFECT_TAP = 30;
var POINTS_GOOD_TAP = 15;
var POINTS_PERFECT_HOLD_START = 10;
var POINTS_GOOD_HOLD_START = 5;
var ACCURACY_POPUP_DURATION = 750;
var ACCURACY_POPUP_Y_OFFSET = -100;
var ACCURACY_POPUP_FONT_SIZE = 80;
var PERFECT_POPUP_COLOR = 0x00FF00;
var GOOD_POPUP_COLOR = 0xFFFF00;
var SCANNER_CYCLE_DURATION = 4000;
var SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2;
var HIT_TOLERANCE_PX_FOR_HOLD_END = 100;
var HOLD_END_GRACE_PERIOD_MS = 250;
var GAME_STATE_TITLE = 'title';
var GAME_STATE_SONG_SELECT = 'songSelect';
var GAME_STATE_PLAYING = 'playing';
var GAME_STATE_RESULTS = 'results';
var GAME_STATE_TUTORIAL = 'tutorial'; // New game state for the tutorial
var currentGameState = GAME_STATE_TITLE;
var resultsScreen = {};
var tutorialScreen = {}; // Holds tutorial screen elements
var activeHoldNotes = {};
var scoreTxt;
var countdownTxt;
var perfectCount = 0;
var goodCount = 0;
var missCount = 0;
var scanner;
var scannerIsMovingUp = true;
var gameStartTime;
var gameStarted = false;
var notes = [];
var currentSongData;
var spawnedNotes = [];
var leftZone, rightZone;
var isDragging = false;
var dragStartX, dragStartY;
var currentTrail = [];
var backgroundParticles = [];
var NUM_BACKGROUND_PARTICLES = 150;
var titleScreen = {};
var songSelectScreen = {};
var songSelectScrollY = 0;
var maxScrollY = 0;
var isDraggingSongList = false;
var dragStartYSongList = 0;
var songSelectionInProgress = false; // Flag to prevent multiple song selections simultaneously
var SONG_ITEM_HEIGHT = 330;
var COLOR_LEFT_ZONE = 0x0077FF;
var COLOR_RIGHT_ZONE = 0xFF3333;
var COLOR_SYNC_TAP = 0xFF69B4;
var COLOR_SYNC_HOLD = 0x9370DB;
var SYNC_NOTE_COLOR = 0xFFA500;
// Helper function to get current particle direction with smooth interpolation
function getCurrentParticleDirection() {
if (!isChangingDirection) {
return globalParticleDirection;
}
var currentTime = Date.now();
var elapsed = currentTime - directionChangeStartTime;
var progress = Math.min(elapsed / directionChangeDuration, 1);
// Smooth easing
var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep
// Interpolate between current and target direction
var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress;
var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress;
// Normalize the direction
var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY);
if (length > 0) {
lerpX /= length;
lerpY /= length;
}
// Check if transition is complete
if (progress >= 1) {
isChangingDirection = false;
globalParticleDirection.x = targetParticleDirection.x;
globalParticleDirection.y = targetParticleDirection.y;
lastDirectionChangeTime = currentTime;
nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds
}
return {
x: lerpX,
y: lerpY
};
}
// Function to initiate a direction change
function changeParticleDirection() {
if (isChangingDirection) {
return;
} // Already changing
// Generate a new direction 45-90 degrees from current
var angleChange = Math.PI / 4 + Math.random() * (Math.PI / 4); // 45-90 degrees
if (Math.random() < 0.5) {
angleChange = -angleChange;
} // Random direction
var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x);
var newAngle = currentAngle + angleChange;
targetParticleDirection.x = Math.cos(newAngle);
targetParticleDirection.y = Math.sin(newAngle);
isChangingDirection = true;
directionChangeStartTime = Date.now();
}
var PatternTemplates = {
tapLeft: function tapLeft(startTime) {
return [{
time: startTime,
type: 'tap',
zone: 'left'
}];
},
tapRight: function tapRight(startTime) {
return [{
time: startTime,
type: 'tap',
zone: 'right'
}];
},
syncTap: function syncTap(startTime) {
return [{
time: startTime,
type: 'tap',
zone: 'left'
}, {
time: startTime,
type: 'tap',
zone: 'right'
}];
},
leftRightTaps: function leftRightTaps(startTime, spacing) {
spacing = spacing || 500;
return [{
time: startTime,
type: 'tap',
zone: 'left'
}, {
time: startTime + spacing,
type: 'tap',
zone: 'right'
}];
},
rightLeftTaps: function rightLeftTaps(startTime, spacing) {
spacing = spacing || 500;
return [{
time: startTime,
type: 'tap',
zone: 'right'
}, {
time: startTime + spacing,
type: 'tap',
zone: 'left'
}];
},
holdLeft: function holdLeft(startTime, duration) {
duration = duration || 1000;
return [{
time: startTime,
type: 'hold',
zone: 'left',
duration: duration
}];
},
holdRight: function holdRight(startTime, duration) {
duration = duration || 1000;
return [{
time: startTime,
type: 'hold',
zone: 'right',
duration: duration
}];
},
syncHold: function syncHold(startTime, duration) {
duration = duration || 1000;
return [{
time: startTime,
type: 'hold',
zone: 'left',
duration: duration
}, {
time: startTime,
type: 'hold',
zone: 'right',
duration: duration
}];
},
tripletTaps: function tripletTaps(startTime, zone, spacing) {
spacing = spacing || 167;
return [{
time: startTime,
type: 'tap',
zone: zone
}, {
time: startTime + spacing,
type: 'tap',
zone: zone
}, {
time: startTime + spacing * 2,
type: 'tap',
zone: zone
}];
},
alternatingTriplets: function alternatingTriplets(startTime, spacing) {
spacing = spacing || 167;
return [{
time: startTime,
type: 'tap',
zone: 'left'
}, {
time: startTime + spacing,
type: 'tap',
zone: 'right'
}, {
time: startTime + spacing * 2,
type: 'tap',
zone: 'left'
}];
},
buildUp: function buildUp(startTime) {
return [{
time: startTime,
type: 'tap',
zone: 'left'
}, {
time: startTime + 250,
type: 'tap',
zone: 'right'
}, {
time: startTime + 500,
type: 'tap',
zone: 'left'
}, {
time: startTime + 625,
type: 'tap',
zone: 'right'
}, {
time: startTime + 750,
type: 'tap',
zone: 'left'
}, {
time: startTime + 875,
type: 'tap',
zone: 'right'
}];
}
};
var SongGenerator = {
generateSong: function generateSong(config) {
var notes = [];
var totalLength = config.totalLength || 202136;
var startTime = config.startDelay || 2000;
notes = this.generateZoneAwareSong(startTime, totalLength);
notes.sort(function (a, b) {
return a.time - b.time;
});
return notes;
},
generateZoneAwareSong: function generateZoneAwareSong(startTime, songTotalLength) {
var notes = [];
var currentTime = startTime;
var zoneFreeTime = {
left: startTime,
right: startTime
};
var MIN_SAME_ZONE_SPACING = 400;
var MIN_DIFFERENT_ZONE_SPACING = 200;
var MIN_SYNC_SPACING = 1000;
var lastSyncTime = startTime - MIN_SYNC_SPACING;
var sections = [{
start: 0,
end: 16000,
complexity: 'simple'
}, {
start: 16000,
end: 48000,
complexity: 'medium'
}, {
start: 48000,
end: 72000,
complexity: 'medium'
}, {
start: 72000,
end: 104000,
complexity: 'medium'
}, {
start: 104000,
end: 128000,
complexity: 'complex'
}, {
start: 128000,
end: 160000,
complexity: 'medium'
}, {
start: 160000,
end: 184000,
complexity: 'complex'
}, {
start: 184000,
end: songTotalLength,
complexity: 'simple'
}];
for (var s = 0; s < sections.length; s++) {
var section = sections[s];
var sectionStartTime = startTime + section.start;
var sectionEndTime = startTime + section.end;
if (sectionEndTime > startTime + songTotalLength) {
sectionEndTime = startTime + songTotalLength;
}
if (sectionStartTime >= sectionEndTime) {
continue;
}
currentTime = Math.max(currentTime, sectionStartTime);
while (currentTime < sectionEndTime - 1000) {
var pattern = this.selectPattern(section.complexity, zoneFreeTime, currentTime, lastSyncTime);
var patternResult = this.placePattern(pattern, currentTime, zoneFreeTime, MIN_SAME_ZONE_SPACING, MIN_DIFFERENT_ZONE_SPACING, sectionEndTime);
if (patternResult.notes.length > 0) {
var allNotesFit = true;
for (var ni = 0; ni < patternResult.notes.length; ni++) {
if (patternResult.notes[ni].time + (patternResult.notes[ni].duration || 0) > sectionEndTime - 200) {
allNotesFit = false;
break;
}
}
if (allNotesFit) {
notes = notes.concat(patternResult.notes);
currentTime = patternResult.nextTime;
if (pattern.type === 'sync' || pattern.type === 'syncHold') {
lastSyncTime = patternResult.notes[0].time;
}
} else {
currentTime += 300;
}
} else {
currentTime += 300;
}
if (currentTime >= sectionEndTime - 1000) {
break;
}
}
}
return notes;
},
selectPattern: function selectPattern(complexity, zoneFreeTime, currentTime, lastSyncTime) {
var patterns = [];
var MIN_SYNC_SPACING_FOR_SELECTION = 1000;
if (complexity === 'simple') {
patterns = [{
type: 'single',
zone: 'left',
duration: 800
}, {
type: 'single',
zone: 'right',
duration: 800
}, {
type: 'rest',
duration: 1200
}, {
type: 'rest',
duration: 1200
}];
if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION + 200) {
patterns.push({
type: 'sync',
duration: 1000
});
}
} else if (complexity === 'medium') {
patterns = [{
type: 'hold',
zone: 'right',
holdDuration: 1000,
duration: 700
}, {
type: 'single',
zone: 'left',
duration: 600
}, {
type: 'single',
zone: 'right',
duration: 600
}, {
type: 'single',
zone: 'left',
duration: 600
}, {
type: 'single',
zone: 'right',
duration: 600
}, {
type: 'alternating',
duration: 750
}, {
type: 'rest',
duration: 1000
}];
if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION) {
patterns.push({
type: 'sync',
duration: 800
});
patterns.push({
type: 'sync',
duration: 800
});
}
} else {
patterns = [{
type: 'hold',
zone: 'right',
holdDuration: 1200,
duration: 800
}, {
type: 'alternating',
duration: 800
}, {
type: 'alternating',
duration: 800
}, {
type: 'single',
zone: 'left',
duration: 700
}, {
type: 'single',
zone: 'right',
duration: 700
}, {
type: 'single',
zone: 'left',
duration: 700
}, {
type: 'single',
zone: 'right',
duration: 700
}];
if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION - 200) {
patterns.push({
type: 'sync',
duration: 600
});
patterns.push({
type: 'sync',
duration: 600
});
patterns.push({
type: 'syncHold',
holdDuration: 1200,
duration: 900
});
}
}
return patterns[Math.floor(Math.random() * patterns.length)];
},
placePattern: function placePattern(pattern, requestedTime, zoneFreeTime, minSameZoneSpacing, minDiffZoneSpacing, sectionEndTime) {
var notes = [];
var earliestTime = requestedTime;
var nextTimeAdvance = pattern.duration || 500;
if (pattern.type === 'single') {
earliestTime = Math.max(requestedTime, zoneFreeTime[pattern.zone]);
if (earliestTime + (pattern.duration || 0) < sectionEndTime) {
notes.push({
time: earliestTime,
type: 'tap',
zone: pattern.zone
});
zoneFreeTime[pattern.zone] = earliestTime + minSameZoneSpacing;
return {
notes: notes,
nextTime: earliestTime + nextTimeAdvance
};
}
} else if (pattern.type === 'sync') {
earliestTime = Math.max(requestedTime, zoneFreeTime.left, zoneFreeTime.right);
if (earliestTime + (pattern.duration || 0) < sectionEndTime) {
notes.push({
time: earliestTime,
type: 'tap',
zone: 'left',
color: SYNC_NOTE_COLOR
});
notes.push({
time: earliestTime,
type: 'tap',
zone: 'right',
color: SYNC_NOTE_COLOR
});
zoneFreeTime.left = earliestTime + minSameZoneSpacing;
zoneFreeTime.right = earliestTime + minSameZoneSpacing;
return {
notes: notes,
nextTime: earliestTime + nextTimeAdvance
};
}
} else if (pattern.type === 'alternating') {
var firstZone = Math.random() < 0.5 ? 'left' : 'right';
var secondZone = firstZone === 'left' ? 'right' : 'left';
var t1 = Math.max(requestedTime, zoneFreeTime[firstZone]);
var t2 = Math.max(t1 + minDiffZoneSpacing, zoneFreeTime[secondZone]);
if (t2 + (pattern.duration || 0) - (t2 - t1) < sectionEndTime) {
notes.push({
time: t1,
type: 'tap',
zone: firstZone
});
notes.push({
time: t2,
type: 'tap',
zone: secondZone
});
zoneFreeTime[firstZone] = t1 + minSameZoneSpacing;
zoneFreeTime[secondZone] = t2 + minSameZoneSpacing;
return {
notes: notes,
nextTime: t1 + nextTimeAdvance
};
}
} else if (pattern.type === 'syncHold') {
earliestTime = Math.max(requestedTime, zoneFreeTime.left, zoneFreeTime.right);
var holdDuration = pattern.holdDuration || 1200;
if (earliestTime + holdDuration < sectionEndTime) {
notes.push({
time: earliestTime,
type: 'hold',
zone: 'left',
duration: holdDuration,
color: SYNC_NOTE_COLOR
});
notes.push({
time: earliestTime,
type: 'hold',
zone: 'right',
duration: holdDuration,
color: SYNC_NOTE_COLOR
});
zoneFreeTime.left = earliestTime + holdDuration + 200;
zoneFreeTime.right = earliestTime + holdDuration + 200;
return {
notes: notes,
nextTime: earliestTime + (pattern.duration || 800)
};
}
} else if (pattern.type === 'hold') {
earliestTime = Math.max(requestedTime, zoneFreeTime[pattern.zone]);
var holdDuration = pattern.holdDuration || 1500;
if (earliestTime + holdDuration < sectionEndTime) {
notes.push({
time: earliestTime,
type: 'hold',
zone: pattern.zone,
duration: holdDuration
});
var otherZone = pattern.zone === 'left' ? 'right' : 'left';
zoneFreeTime[pattern.zone] = earliestTime + holdDuration + 200;
zoneFreeTime[otherZone] = Math.max(zoneFreeTime[otherZone], earliestTime + holdDuration + minDiffZoneSpacing);
return {
notes: notes,
nextTime: earliestTime + nextTimeAdvance
};
}
} else if (pattern.type === 'rest') {
return {
notes: [],
nextTime: requestedTime + (pattern.duration || 500)
};
}
return {
notes: [],
nextTime: requestedTime + 100
};
}
};
function _createTutorialText(text, size, yPos, parent, fill, align, customX) {
var textObj = new Text2(text, {
size: size,
fill: fill || 0xFFFFFF,
wordWrap: true,
wordWrapWidth: GAME_WIDTH - 200,
// 100px padding each side
align: align || 'left'
});
if (align === 'center') {
textObj.anchor.set(0.5, 0);
textObj.x = customX || GAME_WIDTH / 2;
} else {
// left align
textObj.anchor.set(0, 0);
textObj.x = customX || 100;
}
textObj.y = yPos;
parent.addChild(textObj);
return textObj;
}
function _createTutorialNoteVisual(assetId, color, xPos, yPos, scale, parent) {
var noteVisual = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale || 0.35,
scaleY: scale || 0.35
});
noteVisual.tint = color;
noteVisual.x = xPos;
noteVisual.y = yPos;
parent.addChild(noteVisual);
return noteVisual;
}
var SongDatabase = {
'Remember This': {
bpm: 99,
scannerCycleDuration: 4000,
totalLength: 205078,
highScore: storage.highScores && storage.highScores['Remember This'] || 0,
highestGrade: storage.highestGrades && storage.highestGrades['Remember This'] || 'C',
notes: [{
"time": 673,
"type": "tap",
"zone": "right"
}, {
"time": 1277,
"type": "tap",
"zone": "left"
}, {
"time": 2461,
"type": "tap",
"zone": "left"
}, {
"time": 3065,
"type": "tap",
"zone": "right"
}, {
"time": 4249,
"type": "tap",
"zone": "right"
}, {
"time": 4852,
"type": "tap",
"zone": "left"
}, {
"time": 6060,
"type": "tap",
"zone": "left"
}, {
"time": 6664,
"type": "tap",
"zone": "right"
}, {
"time": 7848,
"type": "tap",
"zone": "right"
}, {
"time": 8452,
"type": "tap",
"zone": "left"
}, {
"time": 9659,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 9659,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 10263,
"type": "tap",
"zone": "right"
}, {
"time": 10866,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 12654,
"type": "tap",
"zone": "right"
}, {
"time": 13258,
"type": "tap",
"zone": "left"
}, {
"time": 13862,
"type": "tap",
"zone": "right"
}, {
"time": 14466,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 14466,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 15046,
"type": "tap",
"zone": "right"
}, {
"time": 15650,
"type": "tap",
"zone": "left"
}, {
"time": 16253,
"type": "tap",
"zone": "right"
}, {
"time": 16857,
"type": "tap",
"zone": "left"
}, {
"time": 17257,
"type": "tap",
"zone": "right"
}, {
"time": 18065,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 19853,
"type": "tap",
"zone": "right"
}, {
"time": 20456,
"type": "tap",
"zone": "left"
}, {
"time": 21037,
"type": "tap",
"zone": "right"
}, {
"time": 21640,
"type": "tap",
"zone": "left"
}, {
"time": 22244,
"type": "tap",
"zone": "right"
}, {
"time": 22848,
"type": "tap",
"zone": "left"
}, {
"time": 24055,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 24055,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 24659,
"type": "tap",
"zone": "right"
}, {
"time": 25263,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 27051,
"type": "tap",
"zone": "right"
}, {
"time": 27631,
"type": "tap",
"zone": "left"
}, {
"time": 28258,
"type": "tap",
"zone": "right"
}, {
"time": 28862,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 28862,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 29466,
"type": "tap",
"zone": "right"
}, {
"time": 30046,
"type": "tap",
"zone": "left"
}, {
"time": 31254,
"type": "tap",
"zone": "left"
}, {
"time": 31857,
"type": "tap",
"zone": "right"
}, {
"time": 32461,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 34249,
"type": "tap",
"zone": "right"
}, {
"time": 34853,
"type": "tap",
"zone": "left"
}, {
"time": 35456,
"type": "tap",
"zone": "right"
}, {
"time": 36060,
"type": "tap",
"zone": "left"
}, {
"time": 36641,
"type": "tap",
"zone": "right"
}, {
"time": 37244,
"type": "tap",
"zone": "left"
}, {
"time": 37848,
"type": "tap",
"zone": "right"
}, {
"time": 38475,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 38475,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 39102,
"type": "tap",
"zone": "right"
}, {
"time": 39775,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 41076,
"type": "tap",
"zone": "left"
}, {
"time": 42353,
"type": "tap",
"zone": "left"
}, {
"time": 42956,
"type": "tap",
"zone": "right"
}, {
"time": 44141,
"type": "tap",
"zone": "right"
}, {
"time": 44721,
"type": "tap",
"zone": "left"
}, {
"time": 45325,
"type": "tap",
"zone": "right"
}, {
"time": 45882,
"type": "tap",
"zone": "left"
}, {
"time": 46439,
"type": "tap",
"zone": "right"
}, {
"time": 47531,
"type": "tap",
"zone": "right"
}, {
"time": 48065,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 48065,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 48645,
"type": "tap",
"zone": "right"
}, {
"time": 49249,
"type": "tap",
"zone": "left"
}, {
"time": 49853,
"type": "tap",
"zone": "right"
}, {
"time": 50456,
"type": "tap",
"zone": "left"
}, {
"time": 50856,
"type": "tap",
"zone": "right"
}, {
"time": 51664,
"type": "tap",
"zone": "left"
}, {
"time": 52244,
"type": "tap",
"zone": "right"
}, {
"time": 52848,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 52848,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 53452,
"type": "tap",
"zone": "right"
}, {
"time": 54056,
"type": "tap",
"zone": "left"
}, {
"time": 54659,
"type": "tap",
"zone": "right"
}, {
"time": 55240,
"type": "tap",
"zone": "left"
}, {
"time": 55640,
"type": "tap",
"zone": "right"
}, {
"time": 56447,
"type": "tap",
"zone": "left"
}, {
"time": 57051,
"type": "tap",
"zone": "right"
}, {
"time": 57655,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 57655,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 59443,
"type": "tap",
"zone": "right"
}, {
"time": 60046,
"type": "tap",
"zone": "left"
}, {
"time": 60446,
"type": "tap",
"zone": "right"
}, {
"time": 61254,
"type": "tap",
"zone": "left"
}, {
"time": 61857,
"type": "tap",
"zone": "right"
}, {
"time": 62461,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 62461,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 63065,
"type": "tap",
"zone": "right"
}, {
"time": 63645,
"type": "tap",
"zone": "left"
}, {
"time": 64249,
"type": "tap",
"zone": "right"
}, {
"time": 64853,
"type": "tap",
"zone": "left"
}, {
"time": 65457,
"type": "tap",
"zone": "right"
}, {
"time": 66060,
"type": "tap",
"zone": "left"
}, {
"time": 66664,
"type": "tap",
"zone": "right"
}, {
"time": 67244,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 67244,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 69056,
"type": "tap",
"zone": "right"
}, {
"time": 69659,
"type": "tap",
"zone": "left"
}, {
"time": 70263,
"type": "tap",
"zone": "right"
}, {
"time": 70867,
"type": "tap",
"zone": "left"
}, {
"time": 71447,
"type": "tap",
"zone": "right"
}, {
"time": 72051,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 72051,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 72655,
"type": "tap",
"zone": "right"
}, {
"time": 73258,
"type": "tap",
"zone": "left"
}, {
"time": 73862,
"type": "tap",
"zone": "right"
}, {
"time": 74466,
"type": "tap",
"zone": "left"
}, {
"time": 75046,
"type": "tap",
"zone": "right"
}, {
"time": 75650,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 76858,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 76858,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 78065,
"type": "tap",
"zone": "left"
}, {
"time": 78669,
"type": "tap",
"zone": "right"
}, {
"time": 79876,
"type": "tap",
"zone": "right"
}, {
"time": 80457,
"type": "tap",
"zone": "left"
}, {
"time": 81664,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 81664,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 82245,
"type": "tap",
"zone": "right"
}, {
"time": 82848,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 84056,
"type": "tap",
"zone": "left"
}, {
"time": 84636,
"type": "tap",
"zone": "right"
}, {
"time": 85240,
"type": "tap",
"zone": "left"
}, {
"time": 85844,
"type": "tap",
"zone": "right"
}, {
"time": 86447,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 86447,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 88259,
"type": "tap",
"zone": "right"
}, {
"time": 88862,
"type": "tap",
"zone": "left"
}, {
"time": 89443,
"type": "tap",
"zone": "right"
}, {
"time": 90046,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 91254,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 91254,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 91858,
"type": "tap",
"zone": "right"
}, {
"time": 92461,
"type": "tap",
"zone": "left"
}, {
"time": 93042,
"type": "tap",
"zone": "right"
}, {
"time": 93646,
"type": "tap",
"zone": "left"
}, {
"time": 94249,
"type": "tap",
"zone": "right"
}, {
"time": 94853,
"type": "tap",
"zone": "left"
}, {
"time": 95457,
"type": "tap",
"zone": "right"
}, {
"time": 96060,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 96060,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 96641,
"type": "tap",
"zone": "right"
}, {
"time": 97245,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 98452,
"type": "tap",
"zone": "left"
}, {
"time": 99056,
"type": "tap",
"zone": "right"
}, {
"time": 99660,
"type": "tap",
"zone": "left"
}, {
"time": 100240,
"type": "tap",
"zone": "right"
}, {
"time": 100844,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 100844,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 101447,
"type": "tap",
"zone": "right"
}, {
"time": 102028,
"type": "tap",
"zone": "left"
}, {
"time": 103235,
"type": "tap",
"zone": "left"
}, {
"time": 103862,
"type": "tap",
"zone": "right"
}, {
"time": 105116,
"type": "tap",
"zone": "right"
}, {
"time": 105743,
"type": "tap",
"zone": "left"
}, {
"time": 106881,
"type": "tap",
"zone": "left"
}, {
"time": 107461,
"type": "tap",
"zone": "right"
}, {
"time": 108042,
"type": "tap",
"zone": "left"
}, {
"time": 108646,
"type": "tap",
"zone": "right"
}, {
"time": 109249,
"type": "tap",
"zone": "left"
}, {
"time": 109853,
"type": "tap",
"zone": "right"
}, {
"time": 110457,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 110457,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 111061,
"type": "tap",
"zone": "right"
}, {
"time": 111664,
"type": "tap",
"zone": "left"
}, {
"time": 112245,
"type": "tap",
"zone": "right"
}, {
"time": 112848,
"type": "tap",
"zone": "left"
}, {
"time": 113248,
"type": "tap",
"zone": "right"
}, {
"time": 114056,
"type": "tap",
"zone": "left"
}, {
"time": 114660,
"type": "tap",
"zone": "right"
}, {
"time": 115263,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 115263,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 117051,
"type": "tap",
"zone": "right"
}, {
"time": 117655,
"type": "tap",
"zone": "left"
}, {
"time": 118055,
"type": "tap",
"zone": "right"
}, {
"time": 118862,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 120650,
"type": "tap",
"zone": "right"
}, {
"time": 121254,
"type": "tap",
"zone": "left"
}, {
"time": 121858,
"type": "tap",
"zone": "right"
}, {
"time": 122462,
"type": "tap",
"zone": "left"
}, {
"time": 122862,
"type": "tap",
"zone": "right"
}, {
"time": 123646,
"type": "tap",
"zone": "left"
}, {
"time": 124249,
"type": "tap",
"zone": "right"
}, {
"time": 124853,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 124853,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 125457,
"type": "tap",
"zone": "right"
}, {
"time": 126061,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 128452,
"type": "tap",
"zone": "left"
}, {
"time": 129056,
"type": "tap",
"zone": "right"
}, {
"time": 129660,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 129660,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 130263,
"type": "tap",
"zone": "right"
}, {
"time": 130867,
"type": "tap",
"zone": "left"
}, {
"time": 131448,
"type": "tap",
"zone": "right"
}, {
"time": 132051,
"type": "tap",
"zone": "left"
}, {
"time": 132451,
"type": "tap",
"zone": "right"
}, {
"time": 133259,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 134466,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 134466,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 136254,
"type": "tap",
"zone": "right"
}, {
"time": 136858,
"type": "tap",
"zone": "left"
}, {
"time": 137258,
"type": "tap",
"zone": "right"
}, {
"time": 138042,
"type": "tap",
"zone": "left"
}, {
"time": 138646,
"type": "tap",
"zone": "right"
}, {
"time": 139250,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 139250,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 139853,
"type": "tap",
"zone": "right"
}, {
"time": 140457,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 141664,
"type": "tap",
"zone": "left"
}, {
"time": 142245,
"type": "tap",
"zone": "right"
}, {
"time": 142849,
"type": "tap",
"zone": "left"
}, {
"time": 143452,
"type": "tap",
"zone": "right"
}, {
"time": 144056,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 144056,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 145844,
"type": "tap",
"zone": "right"
}, {
"time": 146448,
"type": "tap",
"zone": "left"
}, {
"time": 147051,
"type": "tap",
"zone": "right"
}, {
"time": 148259,
"type": "tap",
"zone": "right"
}, {
"time": 148839,
"type": "tap",
"zone": "left"
}, {
"time": 150070,
"type": "tap",
"zone": "left"
}, {
"time": 150697,
"type": "tap",
"zone": "right"
}, {
"time": 151904,
"type": "tap",
"zone": "right"
}, {
"time": 152462,
"type": "tap",
"zone": "left"
}, {
"time": 153669,
"type": "tap",
"zone": "left"
}, {
"time": 154273,
"type": "tap",
"zone": "right"
}, {
"time": 155480,
"type": "tap",
"zone": "right"
}, {
"time": 156084,
"type": "tap",
"zone": "left"
}, {
"time": 157268,
"type": "tap",
"zone": "left"
}, {
"time": 157872,
"type": "tap",
"zone": "right"
}, {
"time": 159056,
"type": "tap",
"zone": "right"
}, {
"time": 159613,
"type": "tap",
"zone": "left"
}, {
"time": 160264,
"type": "tap",
"zone": "right"
}, {
"time": 160867,
"type": "tap",
"zone": "left"
}, {
"time": 161448,
"type": "tap",
"zone": "right"
}, {
"time": 162052,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 163259,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 163259,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 163863,
"type": "tap",
"zone": "right"
}, {
"time": 164420,
"type": "tap",
"zone": "left"
}, {
"time": 164977,
"type": "tap",
"zone": "right"
}, {
"time": 165535,
"type": "tap",
"zone": "left"
}, {
"time": 166115,
"type": "tap",
"zone": "right"
}, {
"time": 166719,
"type": "tap",
"zone": "left"
}, {
"time": 167903,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 167903,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 168530,
"type": "tap",
"zone": "right"
}, {
"time": 169250,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 170457,
"type": "tap",
"zone": "left"
}, {
"time": 170857,
"type": "tap",
"zone": "right"
}, {
"time": 171665,
"type": "tap",
"zone": "left"
}, {
"time": 172245,
"type": "tap",
"zone": "right"
}, {
"time": 172849,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 172849,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 174660,
"type": "tap",
"zone": "right"
}, {
"time": 175264,
"type": "tap",
"zone": "left"
}, {
"time": 175664,
"type": "tap",
"zone": "right"
}, {
"time": 176448,
"type": "tap",
"zone": "left"
}, {
"time": 177052,
"type": "tap",
"zone": "right"
}, {
"time": 177655,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 177655,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 178259,
"type": "tap",
"zone": "right"
}, {
"time": 178863,
"type": "tap",
"zone": "left"
}, {
"time": 179443,
"type": "tap",
"zone": "right"
}, {
"time": 180047,
"type": "tap",
"zone": "left"
}, {
"time": 180447,
"type": "tap",
"zone": "right"
}, {
"time": 181254,
"type": "tap",
"zone": "left"
}, {
"time": 181858,
"type": "tap",
"zone": "right"
}, {
"time": 182532,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 182532,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 183205,
"type": "tap",
"zone": "right"
}, {
"time": 184528,
"type": "tap",
"zone": "right"
}, {
"time": 185155,
"type": "tap",
"zone": "left"
}, {
"time": 185555,
"type": "tap",
"zone": "right"
}, {
"time": 186363,
"type": "tap",
"zone": "left"
}, {
"time": 186967,
"type": "tap",
"zone": "right"
}, {
"time": 187547,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 187547,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 188151,
"type": "tap",
"zone": "right"
}, {
"time": 188755,
"type": "tap",
"zone": "left"
}, {
"time": 189358,
"type": "tap",
"zone": "right"
}, {
"time": 189962,
"type": "tap",
"zone": "left"
}, {
"time": 190566,
"type": "tap",
"zone": "right"
}, {
"time": 191146,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 192354,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 192354,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 192957,
"type": "tap",
"zone": "right"
}, {
"time": 193561,
"type": "tap",
"zone": "left"
}, {
"time": 194165,
"type": "tap",
"zone": "right"
}, {
"time": 194745,
"type": "tap",
"zone": "left"
}, {
"time": 195349,
"type": "tap",
"zone": "right"
}, {
"time": 195953,
"type": "tap",
"zone": "left"
}, {
"time": 196556,
"type": "tap",
"zone": "right"
}, {
"time": 197160,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 197160,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 197764,
"type": "tap",
"zone": "right"
}, {
"time": 198344,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 199552,
"type": "tap",
"zone": "left"
}, {
"time": 200156,
"type": "tap",
"zone": "right"
}, {
"time": 200759,
"type": "tap",
"zone": "left"
}]
},
'Arcade Heart': {
bpm: 95,
scannerCycleDuration: 4000,
totalLength: 192446,
highScore: storage.highScores && storage.highScores['Arcade Heart'] || 0,
highestGrade: storage.highestGrades && storage.highestGrades['Arcade Heart'] || 'C',
notes: [{
"time": 1602,
"type": "tap",
"zone": "right"
}, {
"time": 2229,
"type": "tap",
"zone": "left"
}, {
"time": 3482,
"type": "tap",
"zone": "left"
}, {
"time": 4133,
"type": "tap",
"zone": "right"
}, {
"time": 5387,
"type": "tap",
"zone": "right"
}, {
"time": 6013,
"type": "tap",
"zone": "left"
}, {
"time": 7291,
"type": "tap",
"zone": "left"
}, {
"time": 7918,
"type": "tap",
"zone": "right"
}, {
"time": 9171,
"type": "tap",
"zone": "right"
}, {
"time": 9798,
"type": "tap",
"zone": "left"
}, {
"time": 11052,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 11052,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 11702,
"type": "tap",
"zone": "right"
}, {
"time": 12329,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 13583,
"type": "tap",
"zone": "left"
}, {
"time": 14210,
"type": "tap",
"zone": "right"
}, {
"time": 14837,
"type": "tap",
"zone": "left"
}, {
"time": 15487,
"type": "tap",
"zone": "right"
}, {
"time": 16114,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 16114,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 16741,
"type": "tap",
"zone": "right"
}, {
"time": 17368,
"type": "tap",
"zone": "left"
}, {
"time": 18018,
"type": "tap",
"zone": "right"
}, {
"time": 18622,
"type": "tap",
"zone": "left"
}, {
"time": 19272,
"type": "tap",
"zone": "right"
}, {
"time": 19899,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 21153,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 21153,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 21803,
"type": "tap",
"zone": "right"
}, {
"time": 22430,
"type": "tap",
"zone": "left"
}, {
"time": 23057,
"type": "tap",
"zone": "right"
}, {
"time": 23684,
"type": "tap",
"zone": "left"
}, {
"time": 24334,
"type": "tap",
"zone": "right"
}, {
"time": 24938,
"type": "tap",
"zone": "left"
}, {
"time": 25588,
"type": "tap",
"zone": "right"
}, {
"time": 26215,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 26215,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 26865,
"type": "tap",
"zone": "right"
}, {
"time": 27492,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 28746,
"type": "tap",
"zone": "left"
}, {
"time": 29373,
"type": "tap",
"zone": "right"
}, {
"time": 30000,
"type": "tap",
"zone": "left"
}, {
"time": 30650,
"type": "tap",
"zone": "right"
}, {
"time": 31277,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 31277,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 31904,
"type": "tap",
"zone": "right"
}, {
"time": 32531,
"type": "tap",
"zone": "left"
}, {
"time": 33181,
"type": "tap",
"zone": "right"
}, {
"time": 33808,
"type": "tap",
"zone": "left"
}, {
"time": 34435,
"type": "tap",
"zone": "right"
}, {
"time": 35062,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 36316,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 36316,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 36966,
"type": "tap",
"zone": "right"
}, {
"time": 37593,
"type": "tap",
"zone": "left"
}, {
"time": 38220,
"type": "tap",
"zone": "right"
}, {
"time": 38846,
"type": "tap",
"zone": "left"
}, {
"time": 39497,
"type": "tap",
"zone": "right"
}, {
"time": 40124,
"type": "tap",
"zone": "left"
}, {
"time": 40751,
"type": "tap",
"zone": "right"
}, {
"time": 41377,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 41377,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 43281,
"type": "tap",
"zone": "right"
}, {
"time": 43908,
"type": "tap",
"zone": "left"
}, {
"time": 44535,
"type": "tap",
"zone": "right"
}, {
"time": 45162,
"type": "tap",
"zone": "left"
}, {
"time": 45812,
"type": "tap",
"zone": "right"
}, {
"time": 46439,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 46439,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 47066,
"type": "tap",
"zone": "right"
}, {
"time": 47693,
"type": "tap",
"zone": "left"
}, {
"time": 48320,
"type": "tap",
"zone": "right"
}, {
"time": 48947,
"type": "tap",
"zone": "left"
}, {
"time": 49347,
"type": "tap",
"zone": "right"
}, {
"time": 50224,
"type": "tap",
"zone": "left"
}, {
"time": 50851,
"type": "tap",
"zone": "right"
}, {
"time": 51478,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 51478,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 53382,
"type": "tap",
"zone": "right"
}, {
"time": 54009,
"type": "tap",
"zone": "left"
}, {
"time": 54409,
"type": "tap",
"zone": "right"
}, {
"time": 55263,
"type": "tap",
"zone": "left"
}, {
"time": 55913,
"type": "tap",
"zone": "right"
}, {
"time": 56540,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 56540,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 57167,
"type": "tap",
"zone": "right"
}, {
"time": 57794,
"type": "tap",
"zone": "left"
}, {
"time": 58444,
"type": "tap",
"zone": "right"
}, {
"time": 59071,
"type": "tap",
"zone": "left"
}, {
"time": 59698,
"type": "tap",
"zone": "right"
}, {
"time": 60325,
"type": "tap",
"zone": "left"
}, {
"time": 60952,
"type": "tap",
"zone": "right"
}, {
"time": 61602,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 61602,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 62229,
"type": "tap",
"zone": "right"
}, {
"time": 62856,
"type": "tap",
"zone": "left"
}, {
"time": 63483,
"type": "tap",
"zone": "right"
}, {
"time": 64110,
"type": "tap",
"zone": "left"
}, {
"time": 64510,
"type": "tap",
"zone": "right"
}, {
"time": 65387,
"type": "tap",
"zone": "left"
}, {
"time": 66014,
"type": "tap",
"zone": "right"
}, {
"time": 66641,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 66641,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 67268,
"type": "tap",
"zone": "right"
}, {
"time": 67918,
"type": "tap",
"zone": "left"
}, {
"time": 68545,
"type": "tap",
"zone": "right"
}, {
"time": 69799,
"type": "tap",
"zone": "right"
}, {
"time": 70426,
"type": "tap",
"zone": "left"
}, {
"time": 71703,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 71703,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 72330,
"type": "tap",
"zone": "right"
}, {
"time": 72957,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 74210,
"type": "tap",
"zone": "left"
}, {
"time": 74837,
"type": "tap",
"zone": "right"
}, {
"time": 75488,
"type": "tap",
"zone": "left"
}, {
"time": 76115,
"type": "tap",
"zone": "right"
}, {
"time": 76741,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 76741,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 77392,
"type": "tap",
"zone": "right"
}, {
"time": 78019,
"type": "tap",
"zone": "left"
}, {
"time": 78645,
"type": "tap",
"zone": "right"
}, {
"time": 79272,
"type": "tap",
"zone": "left"
}, {
"time": 79899,
"type": "tap",
"zone": "right"
}, {
"time": 80526,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 81803,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 81803,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 82430,
"type": "tap",
"zone": "right"
}, {
"time": 83057,
"type": "tap",
"zone": "left"
}, {
"time": 83684,
"type": "tap",
"zone": "right"
}, {
"time": 84311,
"type": "tap",
"zone": "left"
}, {
"time": 84961,
"type": "tap",
"zone": "right"
}, {
"time": 85588,
"type": "tap",
"zone": "left"
}, {
"time": 86215,
"type": "tap",
"zone": "right"
}, {
"time": 86842,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 86842,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 87492,
"type": "tap",
"zone": "right"
}, {
"time": 88119,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 89373,
"type": "tap",
"zone": "left"
}, {
"time": 90000,
"type": "tap",
"zone": "right"
}, {
"time": 90650,
"type": "tap",
"zone": "left"
}, {
"time": 91277,
"type": "tap",
"zone": "right"
}, {
"time": 91904,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 91904,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 92531,
"type": "tap",
"zone": "right"
}, {
"time": 93158,
"type": "tap",
"zone": "left"
}, {
"time": 93808,
"type": "tap",
"zone": "right"
}, {
"time": 94435,
"type": "tap",
"zone": "left"
}, {
"time": 95062,
"type": "tap",
"zone": "right"
}, {
"time": 95689,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 96966,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 96966,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 97593,
"type": "tap",
"zone": "right"
}, {
"time": 98220,
"type": "tap",
"zone": "left"
}, {
"time": 98870,
"type": "tap",
"zone": "right"
}, {
"time": 100101,
"type": "tap",
"zone": "right"
}, {
"time": 100751,
"type": "tap",
"zone": "left"
}, {
"time": 101378,
"type": "tap",
"zone": "right"
}, {
"time": 102005,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 102005,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 102632,
"type": "tap",
"zone": "right"
}, {
"time": 103282,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 104536,
"type": "tap",
"zone": "left"
}, {
"time": 104936,
"type": "tap",
"zone": "right"
}, {
"time": 105790,
"type": "tap",
"zone": "left"
}, {
"time": 106440,
"type": "tap",
"zone": "right"
}, {
"time": 107067,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 107067,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 107694,
"type": "tap",
"zone": "right"
}, {
"time": 108321,
"type": "tap",
"zone": "left"
}, {
"time": 108948,
"type": "tap",
"zone": "right"
}, {
"time": 109574,
"type": "tap",
"zone": "left"
}, {
"time": 109974,
"type": "tap",
"zone": "right"
}, {
"time": 110852,
"type": "tap",
"zone": "left"
}, {
"time": 111479,
"type": "tap",
"zone": "right"
}, {
"time": 112129,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 112129,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 112756,
"type": "tap",
"zone": "right"
}, {
"time": 113383,
"type": "tap",
"zone": "left"
}, {
"time": 114009,
"type": "tap",
"zone": "right"
}, {
"time": 114636,
"type": "tap",
"zone": "left"
}, {
"time": 115036,
"type": "tap",
"zone": "right"
}, {
"time": 115890,
"type": "tap",
"zone": "left"
}, {
"time": 116540,
"type": "tap",
"zone": "right"
}, {
"time": 117167,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 117167,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 117794,
"type": "tap",
"zone": "right"
}, {
"time": 118421,
"type": "tap",
"zone": "left"
}, {
"time": 119071,
"type": "tap",
"zone": "right"
}, {
"time": 119698,
"type": "tap",
"zone": "left"
}, {
"time": 120302,
"type": "tap",
"zone": "right"
}, {
"time": 120952,
"type": "tap",
"zone": "left"
}, {
"time": 121602,
"type": "tap",
"zone": "right"
}, {
"time": 122229,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 122229,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 122856,
"type": "tap",
"zone": "right"
}, {
"time": 123483,
"type": "tap",
"zone": "left"
}, {
"time": 124110,
"type": "tap",
"zone": "right"
}, {
"time": 124737,
"type": "tap",
"zone": "left"
}, {
"time": 125137,
"type": "tap",
"zone": "right"
}, {
"time": 126014,
"type": "tap",
"zone": "left"
}, {
"time": 126641,
"type": "tap",
"zone": "right"
}, {
"time": 127268,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 127268,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 127895,
"type": "tap",
"zone": "right"
}, {
"time": 128522,
"type": "tap",
"zone": "left"
}, {
"time": 129172,
"type": "tap",
"zone": "right"
}, {
"time": 129799,
"type": "tap",
"zone": "left"
}, {
"time": 130199,
"type": "tap",
"zone": "right"
}, {
"time": 131053,
"type": "tap",
"zone": "left"
}, {
"time": 131703,
"type": "tap",
"zone": "right"
}, {
"time": 132330,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 132330,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 132957,
"type": "tap",
"zone": "right"
}, {
"time": 133584,
"type": "tap",
"zone": "left"
}, {
"time": 134211,
"type": "tap",
"zone": "right"
}, {
"time": 134838,
"type": "tap",
"zone": "left"
}, {
"time": 135238,
"type": "tap",
"zone": "right"
}, {
"time": 136115,
"type": "tap",
"zone": "left"
}, {
"time": 136672,
"type": "tap",
"zone": "right"
}, {
"time": 137856,
"type": "tap",
"zone": "right"
}, {
"time": 138483,
"type": "tap",
"zone": "left"
}, {
"time": 139760,
"type": "tap",
"zone": "left"
}, {
"time": 140387,
"type": "tap",
"zone": "right"
}, {
"time": 141664,
"type": "tap",
"zone": "right"
}, {
"time": 142291,
"type": "tap",
"zone": "left"
}, {
"time": 143592,
"type": "tap",
"zone": "left"
}, {
"time": 144288,
"type": "tap",
"zone": "right"
}, {
"time": 145705,
"type": "tap",
"zone": "right"
}, {
"time": 146308,
"type": "tap",
"zone": "left"
}, {
"time": 147516,
"type": "tap",
"zone": "left"
}, {
"time": 148143,
"type": "tap",
"zone": "right"
}, {
"time": 149397,
"type": "tap",
"zone": "right"
}, {
"time": 150024,
"type": "tap",
"zone": "left"
}, {
"time": 151278,
"type": "tap",
"zone": "left"
}, {
"time": 151904,
"type": "tap",
"zone": "right"
}, {
"time": 152531,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 152531,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 153205,
"type": "tap",
"zone": "right"
}, {
"time": 153948,
"type": "tap",
"zone": "left"
}, {
"time": 154598,
"type": "tap",
"zone": "right"
}, {
"time": 155225,
"type": "tap",
"zone": "left"
}, {
"time": 155852,
"type": "tap",
"zone": "right"
}, {
"time": 156502,
"type": "tap",
"zone": "left"
}, {
"time": 157129,
"type": "tap",
"zone": "right"
}, {
"time": 157756,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 157756,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 158383,
"type": "tap",
"zone": "right"
}, {
"time": 159010,
"type": "tap",
"zone": "left"
}, {
"time": 159567,
"type": "tap",
"zone": "right"
}, {
"time": 160682,
"type": "tap",
"zone": "right"
}, {
"time": 161378,
"type": "tap",
"zone": "left"
}, {
"time": 162005,
"type": "tap",
"zone": "right"
}, {
"time": 162655,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 162655,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 164536,
"type": "tap",
"zone": "right"
}, {
"time": 165163,
"type": "tap",
"zone": "left"
}, {
"time": 165563,
"type": "tap",
"zone": "right"
}, {
"time": 166440,
"type": "tap",
"zone": "left"
}, {
"time": 167067,
"type": "tap",
"zone": "right"
}, {
"time": 167694,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 167694,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 168321,
"type": "tap",
"zone": "right"
}, {
"time": 168948,
"type": "tap",
"zone": "left"
}, {
"time": 169598,
"type": "tap",
"zone": "right"
}, {
"time": 170225,
"type": "tap",
"zone": "left"
}, {
"time": 170625,
"type": "tap",
"zone": "right"
}, {
"time": 171479,
"type": "tap",
"zone": "left"
}, {
"time": 172106,
"type": "tap",
"zone": "right"
}, {
"time": 172756,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 172756,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 174637,
"type": "tap",
"zone": "right"
}, {
"time": 175264,
"type": "tap",
"zone": "left"
}, {
"time": 175664,
"type": "tap",
"zone": "right"
}, {
"time": 176541,
"type": "tap",
"zone": "left"
}, {
"time": 177168,
"type": "tap",
"zone": "right"
}, {
"time": 177795,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 177795,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 178422,
"type": "tap",
"zone": "right"
}, {
"time": 179049,
"type": "tap",
"zone": "left"
}, {
"time": 179629,
"type": "tap",
"zone": "right"
}, {
"time": 180210,
"type": "tap",
"zone": "left"
}, {
"time": 180813,
"type": "tap",
"zone": "right"
}, {
"time": 181440,
"type": "tap",
"zone": "left"
}, {
"time": 182067,
"type": "tap",
"zone": "right"
}, {
"time": 183344,
"type": "tap",
"zone": "right"
}, {
"time": 183994,
"type": "tap",
"zone": "left"
}, {
"time": 185225,
"type": "tap",
"zone": "left"
}, {
"time": 185852,
"type": "tap",
"zone": "right"
}, {
"time": 187129,
"type": "tap",
"zone": "right"
}, {
"time": 187733,
"type": "tap",
"zone": "left"
}]
},
'Pixel Paradise': {
bpm: 117,
scannerCycleDuration: 4000,
totalLength: 202106,
highScore: storage.highScores && storage.highScores['Pixel Paradise'] || 0,
highestGrade: storage.highestGrades && storage.highestGrades['Pixel Paradise'] || 'C',
notes: [{
"time": 2000,
"type": "tap",
"zone": "left"
}, {
"time": 3000,
"type": "tap",
"zone": "left"
}, {
"time": 4000,
"type": "tap",
"zone": "left"
}, {
"time": 4500,
"type": "tap",
"zone": "right"
}, {
"time": 5000,
"type": "tap",
"zone": "left"
}, {
"time": 5500,
"type": "tap",
"zone": "right"
}, {
"time": 6000,
"type": "tap",
"zone": "left",
"color": 0xFFA500
}, {
"time": 6000,
"type": "tap",
"zone": "right",
"color": 0xFFA500
}, {
"time": 7000,
"type": "tap",
"zone": "right"
}, {
"time": 8707,
"type": "tap",
"zone": "right"
}, {
"time": 9218,
"type": "tap",
"zone": "left"
}, {
"time": 9705,
"type": "tap",
"zone": "right"
}, {
"time": 10216,
"type": "tap",
"zone": "left"
}, {
"time": 10704,
"type": "tap",
"zone": "right"
}, {
"time": 11215,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 12701,
"type": "tap",
"zone": "right"
}, {
"time": 13212,
"type": "tap",
"zone": "left"
}, {
"time": 13699,
"type": "tap",
"zone": "right"
}, {
"time": 14210,
"type": "tap",
"zone": "left"
}, {
"time": 14610,
"type": "tap",
"zone": "right"
}, {
"time": 15209,
"type": "tap",
"zone": "left"
}, {
"time": 15696,
"type": "tap",
"zone": "right"
}, {
"time": 16207,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 16207,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 18204,
"type": "tap",
"zone": "left"
}, {
"time": 18692,
"type": "tap",
"zone": "right"
}, {
"time": 19202,
"type": "tap",
"zone": "left"
}, {
"time": 19690,
"type": "tap",
"zone": "right"
}, {
"time": 20201,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 20201,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 20712,
"type": "tap",
"zone": "right"
}, {
"time": 21199,
"type": "tap",
"zone": "left"
}, {
"time": 22198,
"type": "tap",
"zone": "left"
}, {
"time": 22709,
"type": "tap",
"zone": "right"
}, {
"time": 23196,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 24682,
"type": "tap",
"zone": "right"
}, {
"time": 25193,
"type": "tap",
"zone": "left"
}, {
"time": 25704,
"type": "tap",
"zone": "right"
}, {
"time": 26215,
"type": "tap",
"zone": "left"
}, {
"time": 26702,
"type": "tap",
"zone": "right"
}, {
"time": 27213,
"type": "tap",
"zone": "left"
}, {
"time": 27701,
"type": "tap",
"zone": "right"
}, {
"time": 28212,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 28212,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 28699,
"type": "tap",
"zone": "right"
}, {
"time": 29210,
"type": "tap",
"zone": "left"
}, {
"time": 29698,
"type": "tap",
"zone": "right"
}, {
"time": 30209,
"type": "tap",
"zone": "left"
}, {
"time": 30609,
"type": "tap",
"zone": "right"
}, {
"time": 31161,
"type": "tap",
"zone": "left"
}, {
"time": 31672,
"type": "tap",
"zone": "right"
}, {
"time": 32182,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 32182,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 34202,
"type": "tap",
"zone": "left"
}, {
"time": 34602,
"type": "tap",
"zone": "right"
}, {
"time": 35201,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 36710,
"type": "tap",
"zone": "right"
}, {
"time": 37198,
"type": "tap",
"zone": "left"
}, {
"time": 37709,
"type": "tap",
"zone": "right"
}, {
"time": 38196,
"type": "tap",
"zone": "left"
}, {
"time": 38707,
"type": "tap",
"zone": "right"
}, {
"time": 39195,
"type": "tap",
"zone": "left"
}, {
"time": 39682,
"type": "tap",
"zone": "right"
}, {
"time": 40216,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 40216,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 40704,
"type": "tap",
"zone": "right"
}, {
"time": 41215,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 43212,
"type": "tap",
"zone": "left"
}, {
"time": 43699,
"type": "tap",
"zone": "right"
}, {
"time": 44721,
"type": "tap",
"zone": "right"
}, {
"time": 45209,
"type": "tap",
"zone": "left"
}, {
"time": 45696,
"type": "tap",
"zone": "right"
}, {
"time": 46207,
"type": "tap",
"zone": "left"
}, {
"time": 46718,
"type": "tap",
"zone": "right"
}, {
"time": 47693,
"type": "tap",
"zone": "right"
}, {
"time": 48204,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 48204,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 48715,
"type": "tap",
"zone": "right"
}, {
"time": 49203,
"type": "tap",
"zone": "left"
}, {
"time": 49713,
"type": "tap",
"zone": "right"
}, {
"time": 50201,
"type": "tap",
"zone": "left"
}, {
"time": 50712,
"type": "tap",
"zone": "right"
}, {
"time": 51200,
"type": "tap",
"zone": "left"
}, {
"time": 51710,
"type": "tap",
"zone": "right"
}, {
"time": 52221,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 52221,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 52709,
"type": "tap",
"zone": "right"
}, {
"time": 53220,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 54706,
"type": "tap",
"zone": "right"
}, {
"time": 55217,
"type": "tap",
"zone": "left"
}, {
"time": 55704,
"type": "tap",
"zone": "right"
}, {
"time": 56192,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 56192,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 56703,
"type": "tap",
"zone": "right"
}, {
"time": 57190,
"type": "tap",
"zone": "left"
}, {
"time": 57701,
"type": "tap",
"zone": "right"
}, {
"time": 58212,
"type": "tap",
"zone": "left"
}, {
"time": 58700,
"type": "tap",
"zone": "right"
}, {
"time": 59210,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 60696,
"type": "tap",
"zone": "right"
}, {
"time": 61207,
"type": "tap",
"zone": "left"
}, {
"time": 61695,
"type": "tap",
"zone": "right"
}, {
"time": 62183,
"type": "tap",
"zone": "left"
}, {
"time": 62693,
"type": "tap",
"zone": "right"
}, {
"time": 63204,
"type": "tap",
"zone": "left"
}, {
"time": 63715,
"type": "tap",
"zone": "right"
}, {
"time": 64203,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 64203,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 64714,
"type": "tap",
"zone": "right"
}, {
"time": 65201,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 66710,
"type": "tap",
"zone": "right"
}, {
"time": 67198,
"type": "tap",
"zone": "left"
}, {
"time": 67709,
"type": "tap",
"zone": "right"
}, {
"time": 68220,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 68220,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 68707,
"type": "tap",
"zone": "right"
}, {
"time": 69195,
"type": "tap",
"zone": "left"
}, {
"time": 69706,
"type": "tap",
"zone": "right"
}, {
"time": 70217,
"type": "tap",
"zone": "left"
}, {
"time": 70617,
"type": "tap",
"zone": "right"
}, {
"time": 71215,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 72701,
"type": "tap",
"zone": "right"
}, {
"time": 73189,
"type": "tap",
"zone": "left"
}, {
"time": 73700,
"type": "tap",
"zone": "right"
}, {
"time": 74210,
"type": "tap",
"zone": "left"
}, {
"time": 74698,
"type": "tap",
"zone": "right"
}, {
"time": 75209,
"type": "tap",
"zone": "left"
}, {
"time": 75697,
"type": "tap",
"zone": "right"
}, {
"time": 76207,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 76207,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 76695,
"type": "tap",
"zone": "right"
}, {
"time": 77206,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 78692,
"type": "tap",
"zone": "right"
}, {
"time": 79203,
"type": "tap",
"zone": "left"
}, {
"time": 79690,
"type": "tap",
"zone": "right"
}, {
"time": 80201,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 80201,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 80689,
"type": "tap",
"zone": "right"
}, {
"time": 81200,
"type": "tap",
"zone": "left"
}, {
"time": 81711,
"type": "tap",
"zone": "right"
}, {
"time": 82221,
"type": "tap",
"zone": "left"
}, {
"time": 82709,
"type": "tap",
"zone": "right"
}, {
"time": 83197,
"type": "tap",
"zone": "left"
}, {
"time": 83707,
"type": "tap",
"zone": "right"
}, {
"time": 84218,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 84218,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 84706,
"type": "tap",
"zone": "right"
}, {
"time": 85217,
"type": "tap",
"zone": "left"
}, {
"time": 85704,
"type": "tap",
"zone": "right"
}, {
"time": 86215,
"type": "tap",
"zone": "left"
}, {
"time": 86615,
"type": "tap",
"zone": "right"
}, {
"time": 87167,
"type": "tap",
"zone": "left"
}, {
"time": 87678,
"type": "tap",
"zone": "right"
}, {
"time": 88166,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 88166,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 90209,
"type": "tap",
"zone": "left"
}, {
"time": 90609,
"type": "tap",
"zone": "right"
}, {
"time": 91184,
"type": "tap",
"zone": "left"
}, {
"time": 91695,
"type": "tap",
"zone": "right"
}, {
"time": 92206,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 92206,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 92694,
"type": "tap",
"zone": "right"
}, {
"time": 93204,
"type": "tap",
"zone": "left"
}, {
"time": 93692,
"type": "tap",
"zone": "right"
}, {
"time": 94203,
"type": "tap",
"zone": "left"
}, {
"time": 94714,
"type": "tap",
"zone": "right"
}, {
"time": 95201,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 96711,
"type": "tap",
"zone": "right"
}, {
"time": 97221,
"type": "tap",
"zone": "left"
}, {
"time": 97709,
"type": "tap",
"zone": "right"
}, {
"time": 98197,
"type": "tap",
"zone": "left"
}, {
"time": 98708,
"type": "tap",
"zone": "right"
}, {
"time": 99195,
"type": "tap",
"zone": "left"
}, {
"time": 100217,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 100217,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 100704,
"type": "tap",
"zone": "right"
}, {
"time": 101703,
"type": "tap",
"zone": "right"
}, {
"time": 102214,
"type": "tap",
"zone": "left"
}, {
"time": 103212,
"type": "tap",
"zone": "left"
}, {
"time": 103700,
"type": "tap",
"zone": "right"
}, {
"time": 104211,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 104211,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 106208,
"type": "tap",
"zone": "left"
}, {
"time": 106608,
"type": "tap",
"zone": "right"
}, {
"time": 107206,
"type": "tap",
"zone": "left"
}, {
"time": 107694,
"type": "tap",
"zone": "right"
}, {
"time": 108204,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 108204,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 108692,
"type": "tap",
"zone": "right"
}, {
"time": 109203,
"type": "tap",
"zone": "left"
}, {
"time": 109691,
"type": "tap",
"zone": "right"
}, {
"time": 110201,
"type": "tap",
"zone": "left"
}, {
"time": 110601,
"type": "tap",
"zone": "right"
}, {
"time": 111200,
"type": "tap",
"zone": "left"
}, {
"time": 111711,
"type": "tap",
"zone": "right"
}, {
"time": 112198,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 112198,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 114195,
"type": "tap",
"zone": "left"
}, {
"time": 114595,
"type": "tap",
"zone": "right"
}, {
"time": 115194,
"type": "tap",
"zone": "left"
}, {
"time": 115705,
"type": "tap",
"zone": "right"
}, {
"time": 116215,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 116215,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 116703,
"type": "tap",
"zone": "right"
}, {
"time": 117191,
"type": "tap",
"zone": "left"
}, {
"time": 117701,
"type": "tap",
"zone": "right"
}, {
"time": 118189,
"type": "tap",
"zone": "left"
}, {
"time": 118700,
"type": "tap",
"zone": "right"
}, {
"time": 119698,
"type": "tap",
"zone": "right"
}, {
"time": 120209,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 120209,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 122206,
"type": "tap",
"zone": "left"
}, {
"time": 122606,
"type": "tap",
"zone": "right"
}, {
"time": 123205,
"type": "tap",
"zone": "left"
}, {
"time": 123715,
"type": "tap",
"zone": "right"
}, {
"time": 124203,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 124203,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 124714,
"type": "tap",
"zone": "right"
}, {
"time": 125201,
"type": "tap",
"zone": "left"
}, {
"time": 125712,
"type": "tap",
"zone": "right"
}, {
"time": 126200,
"type": "tap",
"zone": "left"
}, {
"time": 126600,
"type": "tap",
"zone": "right"
}, {
"time": 127198,
"type": "tap",
"zone": "left"
}, {
"time": 127709,
"type": "tap",
"zone": "right"
}, {
"time": 128684,
"type": "tap",
"zone": "right"
}, {
"time": 129195,
"type": "tap",
"zone": "left"
}, {
"time": 130217,
"type": "tap",
"zone": "left"
}, {
"time": 130705,
"type": "tap",
"zone": "right"
}, {
"time": 131750,
"type": "tap",
"zone": "right"
}, {
"time": 132260,
"type": "tap",
"zone": "left"
}, {
"time": 133189,
"type": "tap",
"zone": "left"
}, {
"time": 133677,
"type": "tap",
"zone": "right"
}, {
"time": 134675,
"type": "tap",
"zone": "right"
}, {
"time": 135186,
"type": "tap",
"zone": "left"
}, {
"time": 136208,
"type": "tap",
"zone": "left"
}, {
"time": 136719,
"type": "tap",
"zone": "right"
}, {
"time": 137717,
"type": "tap",
"zone": "right"
}, {
"time": 138228,
"type": "tap",
"zone": "left"
}, {
"time": 139226,
"type": "tap",
"zone": "left"
}, {
"time": 139714,
"type": "tap",
"zone": "right"
}, {
"time": 140202,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 140202,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 140712,
"type": "tap",
"zone": "right"
}, {
"time": 141223,
"type": "tap",
"zone": "left"
}, {
"time": 142222,
"type": "tap",
"zone": "left"
}, {
"time": 142709,
"type": "tap",
"zone": "right"
}, {
"time": 143708,
"type": "tap",
"zone": "right"
}, {
"time": 144195,
"type": "tap",
"zone": "left"
}, {
"time": 145194,
"type": "tap",
"zone": "left"
}, {
"time": 145705,
"type": "tap",
"zone": "right"
}, {
"time": 146216,
"type": "tap",
"zone": "left"
}, {
"time": 146703,
"type": "tap",
"zone": "right"
}, {
"time": 147214,
"type": "tap",
"zone": "left"
}, {
"time": 147702,
"type": "tap",
"zone": "right"
}, {
"time": 148212,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 148212,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 148700,
"type": "tap",
"zone": "right"
}, {
"time": 149699,
"type": "tap",
"zone": "right"
}, {
"time": 150209,
"type": "tap",
"zone": "left"
}, {
"time": 151208,
"type": "tap",
"zone": "left"
}, {
"time": 151695,
"type": "tap",
"zone": "right"
}, {
"time": 152206,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 152206,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 154203,
"type": "tap",
"zone": "left"
}, {
"time": 154714,
"type": "tap",
"zone": "right"
}, {
"time": 155202,
"type": "tap",
"zone": "left"
}, {
"time": 155713,
"type": "tap",
"zone": "right"
}, {
"time": 156200,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 156200,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 157222,
"type": "tap",
"zone": "left"
}, {
"time": 157709,
"type": "tap",
"zone": "right"
}, {
"time": 158220,
"type": "tap",
"zone": "left"
}, {
"time": 158708,
"type": "tap",
"zone": "right"
}, {
"time": 159219,
"type": "tap",
"zone": "left"
}, {
"time": 159706,
"type": "tap",
"zone": "right"
}, {
"time": 160217,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 160217,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 160728,
"type": "tap",
"zone": "right"
}, {
"time": 161726,
"type": "tap",
"zone": "right"
}, {
"time": 162214,
"type": "tap",
"zone": "left"
}, {
"time": 163213,
"type": "tap",
"zone": "left"
}, {
"time": 163700,
"type": "tap",
"zone": "right"
}, {
"time": 164211,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 164211,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 164699,
"type": "tap",
"zone": "right"
}, {
"time": 165209,
"type": "tap",
"zone": "left"
}, {
"time": 165697,
"type": "tap",
"zone": "right"
}, {
"time": 166208,
"type": "tap",
"zone": "left"
}, {
"time": 166608,
"type": "tap",
"zone": "right"
}, {
"time": 167206,
"type": "tap",
"zone": "left"
}, {
"time": 167717,
"type": "tap",
"zone": "right"
}, {
"time": 168205,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 168205,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 170202,
"type": "tap",
"zone": "left"
}, {
"time": 170602,
"type": "tap",
"zone": "right"
}, {
"time": 171200,
"type": "tap",
"zone": "left"
}, {
"time": 171711,
"type": "tap",
"zone": "right"
}, {
"time": 172199,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 172199,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 172710,
"type": "tap",
"zone": "right"
}, {
"time": 173197,
"type": "tap",
"zone": "left"
}, {
"time": 173708,
"type": "tap",
"zone": "right"
}, {
"time": 174196,
"type": "tap",
"zone": "left"
}, {
"time": 175217,
"type": "tap",
"zone": "left"
}, {
"time": 175705,
"type": "tap",
"zone": "right"
}, {
"time": 176216,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 176216,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 176703,
"type": "tap",
"zone": "right"
}, {
"time": 177214,
"type": "tap",
"zone": "left"
}, {
"time": 177702,
"type": "tap",
"zone": "right"
}, {
"time": 178213,
"type": "tap",
"zone": "left"
}, {
"time": 178700,
"type": "tap",
"zone": "right"
}, {
"time": 179211,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 180697,
"type": "tap",
"zone": "right"
}, {
"time": 181208,
"type": "tap",
"zone": "left"
}, {
"time": 181719,
"type": "tap",
"zone": "right"
}, {
"time": 182206,
"type": "tap",
"zone": "left"
}, {
"time": 182606,
"type": "tap",
"zone": "right"
}, {
"time": 183205,
"type": "tap",
"zone": "left"
}, {
"time": 183716,
"type": "tap",
"zone": "right"
}, {
"time": 184203,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 184203,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 184714,
"type": "tap",
"zone": "right"
}, {
"time": 185202,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 186711,
"type": "tap",
"zone": "right"
}, {
"time": 187222,
"type": "tap",
"zone": "left"
}, {
"time": 187710,
"type": "tap",
"zone": "right"
}, {
"time": 188197,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 188197,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 188708,
"type": "tap",
"zone": "right"
}, {
"time": 189219,
"type": "tap",
"zone": "left"
}, {
"time": 189823,
"type": "tap",
"zone": "right"
}, {
"time": 190403,
"type": "tap",
"zone": "left"
}, {
"time": 190803,
"type": "tap",
"zone": "right"
}, {
"time": 191448,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 193468,
"type": "tap",
"zone": "left"
}, {
"time": 193956,
"type": "tap",
"zone": "right"
}, {
"time": 194954,
"type": "tap",
"zone": "right"
}, {
"time": 195465,
"type": "tap",
"zone": "left"
}, {
"time": 196464,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 196464,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 196974,
"type": "tap",
"zone": "right"
}, {
"time": 197973,
"type": "tap",
"zone": "right"
}, {
"time": 198460,
"type": "tap",
"zone": "left"
}, {
"time": 198971,
"type": "tap",
"zone": "right"
}]
},
'Runner': {
bpm: 92,
scannerCycleDuration: 4000,
totalLength: 230341,
highScore: storage.highScores && storage.highScores['Runner'] || 0,
highestGrade: storage.highestGrades && storage.highestGrades['Runner'] || 'C',
notes: [{
"time": 208,
"type": "tap",
"zone": "left"
}, {
"time": 1671,
"type": "tap",
"zone": "left"
}, {
"time": 2972,
"type": "tap",
"zone": "left"
}, {
"time": 4272,
"type": "tap",
"zone": "left"
}, {
"time": 5247,
"type": "tap",
"zone": "right"
}, {
"time": 5572,
"type": "tap",
"zone": "left"
}, {
"time": 6873,
"type": "tap",
"zone": "left"
}, {
"time": 8173,
"type": "tap",
"zone": "left"
}, {
"time": 9496,
"type": "tap",
"zone": "left"
}, {
"time": 10472,
"type": "tap",
"zone": "right"
}, {
"time": 10797,
"type": "tap",
"zone": "left"
}, {
"time": 11772,
"type": "tap",
"zone": "right"
}, {
"time": 12097,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 13397,
"type": "tap",
"zone": "left"
}, {
"time": 13722,
"type": "tap",
"zone": "right"
}, {
"time": 14048,
"type": "tap",
"zone": "left"
}, {
"time": 14721,
"type": "tap",
"zone": "left"
}, {
"time": 15046,
"type": "tap",
"zone": "right"
}, {
"time": 16021,
"type": "tap",
"zone": "left"
}, {
"time": 17322,
"type": "tap",
"zone": "left"
}, {
"time": 17647,
"type": "tap",
"zone": "right"
}, {
"time": 18622,
"type": "tap",
"zone": "left"
}, {
"time": 18947,
"type": "tap",
"zone": "right"
}, {
"time": 19272,
"type": "tap",
"zone": "left"
}, {
"time": 19945,
"type": "tap",
"zone": "left"
}, {
"time": 21223,
"type": "tap",
"zone": "left"
}, {
"time": 21548,
"type": "tap",
"zone": "right"
}, {
"time": 22523,
"type": "tap",
"zone": "left"
}, {
"time": 23846,
"type": "tap",
"zone": "left"
}, {
"time": 25147,
"type": "tap",
"zone": "left"
}, {
"time": 26447,
"type": "tap",
"zone": "left"
}, {
"time": 26772,
"type": "tap",
"zone": "right"
}, {
"time": 27747,
"type": "tap",
"zone": "left"
}, {
"time": 29048,
"type": "tap",
"zone": "left"
}, {
"time": 30348,
"type": "tap",
"zone": "left"
}, {
"time": 31672,
"type": "tap",
"zone": "left"
}, {
"time": 31997,
"type": "tap",
"zone": "right"
}, {
"time": 32322,
"type": "tap",
"zone": "left"
}, {
"time": 32647,
"type": "tap",
"zone": "right"
}, {
"time": 32972,
"type": "tap",
"zone": "left"
}, {
"time": 33947,
"type": "tap",
"zone": "right"
}, {
"time": 34272,
"type": "tap",
"zone": "left"
}, {
"time": 34597,
"type": "tap",
"zone": "right"
}, {
"time": 34922,
"type": "tap",
"zone": "left"
}, {
"time": 35247,
"type": "tap",
"zone": "right"
}, {
"time": 35572,
"type": "tap",
"zone": "left"
}, {
"time": 36873,
"type": "tap",
"zone": "left"
}, {
"time": 38173,
"type": "tap",
"zone": "left"
}, {
"time": 39497,
"type": "tap",
"zone": "left"
}, {
"time": 40797,
"type": "tap",
"zone": "left"
}, {
"time": 42097,
"type": "tap",
"zone": "left"
}, {
"time": 42422,
"type": "tap",
"zone": "right"
}, {
"time": 42747,
"type": "tap",
"zone": "left"
}, {
"time": 43073,
"type": "tap",
"zone": "right"
}, {
"time": 43398,
"type": "tap",
"zone": "left"
}, {
"time": 43723,
"type": "tap",
"zone": "right"
}, {
"time": 44048,
"type": "tap",
"zone": "left"
}, {
"time": 44373,
"type": "tap",
"zone": "right"
}, {
"time": 44698,
"type": "tap",
"zone": "left"
}, {
"time": 45023,
"type": "tap",
"zone": "right"
}, {
"time": 45348,
"type": "tap",
"zone": "left"
}, {
"time": 45673,
"type": "tap",
"zone": "right"
}, {
"time": 45998,
"type": "tap",
"zone": "left"
}, {
"time": 46323,
"type": "tap",
"zone": "right"
}, {
"time": 46648,
"type": "tap",
"zone": "left"
}, {
"time": 46985,
"type": "tap",
"zone": "right"
}, {
"time": 47322,
"type": "tap",
"zone": "left"
}, {
"time": 47647,
"type": "tap",
"zone": "right"
}, {
"time": 47972,
"type": "tap",
"zone": "left"
}, {
"time": 48622,
"type": "tap",
"zone": "left"
}, {
"time": 48947,
"type": "tap",
"zone": "right"
}, {
"time": 49272,
"type": "tap",
"zone": "left"
}, {
"time": 49597,
"type": "tap",
"zone": "right"
}, {
"time": 49922,
"type": "tap",
"zone": "left"
}, {
"time": 50247,
"type": "tap",
"zone": "right"
}, {
"time": 50573,
"type": "tap",
"zone": "left"
}, {
"time": 51223,
"type": "tap",
"zone": "left"
}, {
"time": 51896,
"type": "tap",
"zone": "left"
}, {
"time": 52221,
"type": "tap",
"zone": "right"
}, {
"time": 52546,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 52546,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 52871,
"type": "tap",
"zone": "right"
}, {
"time": 53196,
"type": "tap",
"zone": "left"
}, {
"time": 53521,
"type": "tap",
"zone": "right"
}, {
"time": 53847,
"type": "tap",
"zone": "left"
}, {
"time": 54172,
"type": "tap",
"zone": "right"
}, {
"time": 54822,
"type": "tap",
"zone": "right"
}, {
"time": 55147,
"type": "tap",
"zone": "left"
}, {
"time": 55472,
"type": "tap",
"zone": "right"
}, {
"time": 56122,
"type": "tap",
"zone": "right"
}, {
"time": 56447,
"type": "tap",
"zone": "left"
}, {
"time": 56772,
"type": "tap",
"zone": "right"
}, {
"time": 57422,
"type": "tap",
"zone": "right"
}, {
"time": 57748,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 57748,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 58073,
"type": "tap",
"zone": "right"
}, {
"time": 58398,
"type": "tap",
"zone": "left"
}, {
"time": 58723,
"type": "tap",
"zone": "right"
}, {
"time": 59048,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 60371,
"type": "tap",
"zone": "left"
}, {
"time": 60696,
"type": "tap",
"zone": "right"
}, {
"time": 61022,
"type": "tap",
"zone": "left"
}, {
"time": 61347,
"type": "tap",
"zone": "right"
}, {
"time": 61672,
"type": "tap",
"zone": "left"
}, {
"time": 61997,
"type": "tap",
"zone": "right"
}, {
"time": 62322,
"type": "tap",
"zone": "left"
}, {
"time": 62647,
"type": "tap",
"zone": "right"
}, {
"time": 62972,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 62972,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 63297,
"type": "tap",
"zone": "right"
}, {
"time": 63622,
"type": "tap",
"zone": "left"
}, {
"time": 63947,
"type": "tap",
"zone": "right"
}, {
"time": 64272,
"type": "tap",
"zone": "left"
}, {
"time": 64597,
"type": "tap",
"zone": "right"
}, {
"time": 64922,
"type": "tap",
"zone": "left"
}, {
"time": 65248,
"type": "tap",
"zone": "right"
}, {
"time": 65573,
"type": "tap",
"zone": "left"
}, {
"time": 65898,
"type": "tap",
"zone": "right"
}, {
"time": 66223,
"type": "tap",
"zone": "left"
}, {
"time": 66548,
"type": "tap",
"zone": "right"
}, {
"time": 66873,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 67871,
"type": "tap",
"zone": "right"
}, {
"time": 68197,
"type": "tap",
"zone": "left"
}, {
"time": 68522,
"type": "tap",
"zone": "right"
}, {
"time": 69172,
"type": "tap",
"zone": "right"
}, {
"time": 69497,
"type": "tap",
"zone": "left"
}, {
"time": 69822,
"type": "tap",
"zone": "right"
}, {
"time": 70147,
"type": "tap",
"zone": "left"
}, {
"time": 70472,
"type": "tap",
"zone": "right"
}, {
"time": 70797,
"type": "tap",
"zone": "left"
}, {
"time": 71122,
"type": "tap",
"zone": "right"
}, {
"time": 71447,
"type": "tap",
"zone": "left"
}, {
"time": 71772,
"type": "tap",
"zone": "right"
}, {
"time": 72097,
"type": "tap",
"zone": "left"
}, {
"time": 73398,
"type": "tap",
"zone": "left"
}, {
"time": 73723,
"type": "tap",
"zone": "right"
}, {
"time": 74048,
"type": "tap",
"zone": "left"
}, {
"time": 74373,
"type": "tap",
"zone": "right"
}, {
"time": 74698,
"type": "tap",
"zone": "left"
}, {
"time": 76022,
"type": "tap",
"zone": "left"
}, {
"time": 76347,
"type": "tap",
"zone": "right"
}, {
"time": 76997,
"type": "tap",
"zone": "right"
}, {
"time": 77322,
"type": "tap",
"zone": "left"
}, {
"time": 78297,
"type": "tap",
"zone": "right"
}, {
"time": 78622,
"type": "tap",
"zone": "left"
}, {
"time": 78947,
"type": "tap",
"zone": "right"
}, {
"time": 79272,
"type": "tap",
"zone": "left"
}, {
"time": 79923,
"type": "tap",
"zone": "left"
}, {
"time": 81223,
"type": "tap",
"zone": "left"
}, {
"time": 81548,
"type": "tap",
"zone": "right"
}, {
"time": 81873,
"type": "tap",
"zone": "left"
}, {
"time": 82198,
"type": "tap",
"zone": "right"
}, {
"time": 82523,
"type": "tap",
"zone": "left"
}, {
"time": 83173,
"type": "tap",
"zone": "left"
}, {
"time": 83510,
"type": "tap",
"zone": "right"
}, {
"time": 83847,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 83847,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 84172,
"type": "tap",
"zone": "right"
}, {
"time": 84497,
"type": "tap",
"zone": "left"
}, {
"time": 84822,
"type": "tap",
"zone": "right"
}, {
"time": 85147,
"type": "tap",
"zone": "left"
}, {
"time": 85472,
"type": "tap",
"zone": "right"
}, {
"time": 85797,
"type": "tap",
"zone": "left"
}, {
"time": 86122,
"type": "tap",
"zone": "right"
}, {
"time": 86447,
"type": "tap",
"zone": "left"
}, {
"time": 86772,
"type": "tap",
"zone": "right"
}, {
"time": 87098,
"type": "tap",
"zone": "left"
}, {
"time": 87423,
"type": "tap",
"zone": "right"
}, {
"time": 87748,
"type": "tap",
"zone": "left"
}, {
"time": 88073,
"type": "tap",
"zone": "right"
}, {
"time": 88398,
"type": "tap",
"zone": "left"
}, {
"time": 88723,
"type": "tap",
"zone": "right"
}, {
"time": 89048,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 89048,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 89373,
"type": "tap",
"zone": "right"
}, {
"time": 90348,
"type": "tap",
"zone": "left"
}, {
"time": 91335,
"type": "tap",
"zone": "right"
}, {
"time": 91672,
"type": "tap",
"zone": "left"
}, {
"time": 91997,
"type": "tap",
"zone": "right"
}, {
"time": 92322,
"type": "tap",
"zone": "left"
}, {
"time": 92647,
"type": "tap",
"zone": "right"
}, {
"time": 92972,
"type": "tap",
"zone": "left"
}, {
"time": 93297,
"type": "tap",
"zone": "right"
}, {
"time": 93622,
"type": "tap",
"zone": "left"
}, {
"time": 93947,
"type": "tap",
"zone": "right"
}, {
"time": 94273,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 94273,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 94598,
"type": "tap",
"zone": "right"
}, {
"time": 94923,
"type": "tap",
"zone": "left"
}, {
"time": 95248,
"type": "tap",
"zone": "right"
}, {
"time": 95573,
"type": "tap",
"zone": "left"
}, {
"time": 95898,
"type": "tap",
"zone": "right"
}, {
"time": 96223,
"type": "tap",
"zone": "left"
}, {
"time": 96548,
"type": "tap",
"zone": "right"
}, {
"time": 96873,
"type": "tap",
"zone": "left"
}, {
"time": 97198,
"type": "tap",
"zone": "right"
}, {
"time": 97523,
"type": "tap",
"zone": "left"
}, {
"time": 97848,
"type": "tap",
"zone": "right"
}, {
"time": 98173,
"type": "tap",
"zone": "left"
}, {
"time": 98499,
"type": "tap",
"zone": "right"
}, {
"time": 98824,
"type": "tap",
"zone": "left"
}, {
"time": 99160,
"type": "tap",
"zone": "right"
}, {
"time": 99497,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 99497,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 99822,
"type": "tap",
"zone": "right"
}, {
"time": 100147,
"type": "tap",
"zone": "left"
}, {
"time": 100472,
"type": "tap",
"zone": "right"
}, {
"time": 100797,
"type": "tap",
"zone": "left"
}, {
"time": 101122,
"type": "tap",
"zone": "right"
}, {
"time": 101447,
"type": "tap",
"zone": "left"
}, {
"time": 101773,
"type": "tap",
"zone": "right"
}, {
"time": 102098,
"type": "tap",
"zone": "left"
}, {
"time": 103421,
"type": "tap",
"zone": "left"
}, {
"time": 104396,
"type": "tap",
"zone": "right"
}, {
"time": 104721,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 104721,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 106672,
"type": "tap",
"zone": "left"
}, {
"time": 106997,
"type": "tap",
"zone": "right"
}, {
"time": 107322,
"type": "tap",
"zone": "left"
}, {
"time": 107647,
"type": "tap",
"zone": "right"
}, {
"time": 107972,
"type": "tap",
"zone": "left"
}, {
"time": 108297,
"type": "tap",
"zone": "right"
}, {
"time": 108622,
"type": "tap",
"zone": "left"
}, {
"time": 108948,
"type": "tap",
"zone": "right"
}, {
"time": 109273,
"type": "tap",
"zone": "left"
}, {
"time": 109598,
"type": "tap",
"zone": "right"
}, {
"time": 109923,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 109923,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 110248,
"type": "tap",
"zone": "right"
}, {
"time": 110573,
"type": "tap",
"zone": "left"
}, {
"time": 110898,
"type": "tap",
"zone": "right"
}, {
"time": 111223,
"type": "tap",
"zone": "left"
}, {
"time": 111548,
"type": "tap",
"zone": "right"
}, {
"time": 111873,
"type": "tap",
"zone": "left"
}, {
"time": 112210,
"type": "tap",
"zone": "right"
}, {
"time": 112547,
"type": "tap",
"zone": "left"
}, {
"time": 112872,
"type": "tap",
"zone": "right"
}, {
"time": 113197,
"type": "tap",
"zone": "left"
}, {
"time": 113522,
"type": "tap",
"zone": "right"
}, {
"time": 113847,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 114822,
"type": "tap",
"zone": "right"
}, {
"time": 115147,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 115147,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 117098,
"type": "tap",
"zone": "left"
}, {
"time": 117423,
"type": "tap",
"zone": "right"
}, {
"time": 117748,
"type": "tap",
"zone": "left"
}, {
"time": 118073,
"type": "tap",
"zone": "right"
}, {
"time": 118398,
"type": "tap",
"zone": "left"
}, {
"time": 118723,
"type": "tap",
"zone": "right"
}, {
"time": 119048,
"type": "tap",
"zone": "left"
}, {
"time": 119385,
"type": "tap",
"zone": "right"
}, {
"time": 119722,
"type": "tap",
"zone": "left"
}, {
"time": 120047,
"type": "tap",
"zone": "right"
}, {
"time": 120372,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 120372,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 120697,
"type": "tap",
"zone": "right"
}, {
"time": 121022,
"type": "tap",
"zone": "left"
}, {
"time": 121347,
"type": "tap",
"zone": "right"
}, {
"time": 121672,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 122322,
"type": "tap",
"zone": "left"
}, {
"time": 122972,
"type": "tap",
"zone": "left"
}, {
"time": 123297,
"type": "tap",
"zone": "right"
}, {
"time": 123623,
"type": "tap",
"zone": "left"
}, {
"time": 123948,
"type": "tap",
"zone": "right"
}, {
"time": 124273,
"type": "tap",
"zone": "left"
}, {
"time": 125573,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 125573,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 125898,
"type": "tap",
"zone": "right"
}, {
"time": 126548,
"type": "tap",
"zone": "right"
}, {
"time": 126873,
"type": "tap",
"zone": "left"
}, {
"time": 127198,
"type": "tap",
"zone": "right"
}, {
"time": 127523,
"type": "tap",
"zone": "left"
}, {
"time": 127849,
"type": "tap",
"zone": "right"
}, {
"time": 128174,
"type": "tap",
"zone": "left"
}, {
"time": 128510,
"type": "tap",
"zone": "right"
}, {
"time": 129172,
"type": "tap",
"zone": "right"
}, {
"time": 129497,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 130472,
"type": "tap",
"zone": "right"
}, {
"time": 130798,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 130798,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 131123,
"type": "tap",
"zone": "right"
}, {
"time": 131448,
"type": "tap",
"zone": "left"
}, {
"time": 131773,
"type": "tap",
"zone": "right"
}, {
"time": 132098,
"type": "tap",
"zone": "left"
}, {
"time": 132423,
"type": "tap",
"zone": "right"
}, {
"time": 132748,
"type": "tap",
"zone": "left"
}, {
"time": 133073,
"type": "tap",
"zone": "right"
}, {
"time": 133398,
"type": "tap",
"zone": "left"
}, {
"time": 133723,
"type": "tap",
"zone": "right"
}, {
"time": 134373,
"type": "tap",
"zone": "right"
}, {
"time": 134698,
"type": "tap",
"zone": "left"
}, {
"time": 135035,
"type": "tap",
"zone": "right"
}, {
"time": 135372,
"type": "tap",
"zone": "left"
}, {
"time": 135697,
"type": "tap",
"zone": "right"
}, {
"time": 136022,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 136022,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 136347,
"type": "tap",
"zone": "right"
}, {
"time": 136672,
"type": "tap",
"zone": "left"
}, {
"time": 136997,
"type": "tap",
"zone": "right"
}, {
"time": 137322,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 138623,
"type": "tap",
"zone": "left"
}, {
"time": 138948,
"type": "tap",
"zone": "right"
}, {
"time": 139598,
"type": "tap",
"zone": "right"
}, {
"time": 139923,
"type": "tap",
"zone": "left"
}, {
"time": 140248,
"type": "tap",
"zone": "right"
}, {
"time": 140573,
"type": "tap",
"zone": "left"
}, {
"time": 140898,
"type": "tap",
"zone": "right"
}, {
"time": 141223,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 141223,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 141548,
"type": "tap",
"zone": "right"
}, {
"time": 141873,
"type": "tap",
"zone": "left"
}, {
"time": 142199,
"type": "tap",
"zone": "right"
}, {
"time": 142524,
"type": "tap",
"zone": "left"
}, {
"time": 142849,
"type": "tap",
"zone": "right"
}, {
"time": 143174,
"type": "tap",
"zone": "left"
}, {
"time": 143510,
"type": "tap",
"zone": "right"
}, {
"time": 143847,
"type": "tap",
"zone": "left"
}, {
"time": 144172,
"type": "tap",
"zone": "right"
}, {
"time": 144497,
"type": "tap",
"zone": "left"
}, {
"time": 144822,
"type": "tap",
"zone": "right"
}, {
"time": 145147,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 146123,
"type": "tap",
"zone": "right"
}, {
"time": 146448,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 146448,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 146773,
"type": "tap",
"zone": "right"
}, {
"time": 147098,
"type": "tap",
"zone": "left"
}, {
"time": 147423,
"type": "tap",
"zone": "right"
}, {
"time": 147748,
"type": "tap",
"zone": "left"
}, {
"time": 148073,
"type": "tap",
"zone": "right"
}, {
"time": 148398,
"type": "tap",
"zone": "left"
}, {
"time": 148723,
"type": "tap",
"zone": "right"
}, {
"time": 149048,
"type": "tap",
"zone": "left"
}, {
"time": 149373,
"type": "tap",
"zone": "right"
}, {
"time": 149699,
"type": "tap",
"zone": "left"
}, {
"time": 150024,
"type": "tap",
"zone": "right"
}, {
"time": 150349,
"type": "tap",
"zone": "left"
}, {
"time": 150685,
"type": "tap",
"zone": "right"
}, {
"time": 151022,
"type": "tap",
"zone": "left"
}, {
"time": 151347,
"type": "tap",
"zone": "right"
}, {
"time": 151672,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 151672,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 151997,
"type": "tap",
"zone": "right"
}, {
"time": 152322,
"type": "tap",
"zone": "left"
}, {
"time": 152647,
"type": "tap",
"zone": "right"
}, {
"time": 152973,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 154273,
"type": "tap",
"zone": "left"
}, {
"time": 154598,
"type": "tap",
"zone": "right"
}, {
"time": 154923,
"type": "tap",
"zone": "left"
}, {
"time": 155573,
"type": "tap",
"zone": "left"
}, {
"time": 156874,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 156874,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 158174,
"type": "tap",
"zone": "left"
}, {
"time": 159161,
"type": "tap",
"zone": "right"
}, {
"time": 159497,
"type": "tap",
"zone": "left"
}, {
"time": 159822,
"type": "tap",
"zone": "right"
}, {
"time": 160148,
"type": "tap",
"zone": "left"
}, {
"time": 160473,
"type": "tap",
"zone": "right"
}, {
"time": 160798,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 161773,
"type": "tap",
"zone": "right"
}, {
"time": 162098,
"type": "tap",
"zone": "left"
}, {
"time": 163398,
"type": "tap",
"zone": "left"
}, {
"time": 164374,
"type": "tap",
"zone": "right"
}, {
"time": 164699,
"type": "tap",
"zone": "left"
}, {
"time": 165024,
"type": "tap",
"zone": "right"
}, {
"time": 165349,
"type": "tap",
"zone": "left"
}, {
"time": 165674,
"type": "tap",
"zone": "right"
}, {
"time": 165999,
"type": "tap",
"zone": "left"
}, {
"time": 166324,
"type": "tap",
"zone": "right"
}, {
"time": 166649,
"type": "tap",
"zone": "left"
}, {
"time": 166986,
"type": "tap",
"zone": "right"
}, {
"time": 167322,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 167322,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 167973,
"type": "tap",
"zone": "left"
}, {
"time": 168623,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 169923,
"type": "tap",
"zone": "left"
}, {
"time": 170573,
"type": "tap",
"zone": "left"
}, {
"time": 171223,
"type": "tap",
"zone": "left"
}, {
"time": 171874,
"type": "tap",
"zone": "left"
}, {
"time": 172199,
"type": "tap",
"zone": "right"
}, {
"time": 172524,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 172524,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 172849,
"type": "tap",
"zone": "right"
}, {
"time": 173174,
"type": "tap",
"zone": "left"
}, {
"time": 173499,
"type": "tap",
"zone": "right"
}, {
"time": 173824,
"type": "tap",
"zone": "left"
}, {
"time": 174161,
"type": "tap",
"zone": "right"
}, {
"time": 174497,
"type": "tap",
"zone": "left"
}, {
"time": 175148,
"type": "tap",
"zone": "left"
}, {
"time": 175473,
"type": "tap",
"zone": "right"
}, {
"time": 175798,
"type": "tap",
"zone": "left"
}, {
"time": 176587,
"type": "tap",
"zone": "left"
}, {
"time": 177528,
"type": "tap",
"zone": "right"
}, {
"time": 177818,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 177818,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 179699,
"type": "tap",
"zone": "left"
}, {
"time": 180024,
"type": "tap",
"zone": "right"
}, {
"time": 180349,
"type": "tap",
"zone": "left"
}, {
"time": 180674,
"type": "tap",
"zone": "right"
}, {
"time": 180999,
"type": "tap",
"zone": "left"
}, {
"time": 181324,
"type": "tap",
"zone": "right"
}, {
"time": 181649,
"type": "tap",
"zone": "left"
}, {
"time": 181974,
"type": "tap",
"zone": "right"
}, {
"time": 182299,
"type": "tap",
"zone": "left"
}, {
"time": 182624,
"type": "tap",
"zone": "right"
}, {
"time": 182950,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 182950,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 183275,
"type": "tap",
"zone": "right"
}, {
"time": 183600,
"type": "tap",
"zone": "left"
}, {
"time": 183925,
"type": "tap",
"zone": "right"
}, {
"time": 184250,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 185550,
"type": "tap",
"zone": "left"
}, {
"time": 185887,
"type": "tap",
"zone": "right"
}, {
"time": 186224,
"type": "tap",
"zone": "left"
}, {
"time": 186549,
"type": "tap",
"zone": "right"
}, {
"time": 186874,
"type": "tap",
"zone": "left"
}, {
"time": 187524,
"type": "tap",
"zone": "left"
}, {
"time": 187849,
"type": "tap",
"zone": "right"
}, {
"time": 188174,
"type": "hold",
"zone": "left",
"duration": 1500,
"color": 16753920
}, {
"time": 188174,
"type": "hold",
"zone": "right",
"duration": 1500,
"color": 16753920
}, {
"time": 190124,
"type": "tap",
"zone": "left"
}, {
"time": 190450,
"type": "tap",
"zone": "right"
}, {
"time": 190775,
"type": "tap",
"zone": "left"
}, {
"time": 191100,
"type": "tap",
"zone": "right"
}, {
"time": 191425,
"type": "tap",
"zone": "left"
}, {
"time": 191750,
"type": "tap",
"zone": "right"
}, {
"time": 192075,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 192400,
"type": "tap",
"zone": "right"
}, {
"time": 193399,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 193399,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 194699,
"type": "tap",
"zone": "left"
}, {
"time": 195674,
"type": "tap",
"zone": "right"
}, {
"time": 195999,
"type": "tap",
"zone": "left"
}, {
"time": 196324,
"type": "tap",
"zone": "right"
}, {
"time": 196649,
"type": "tap",
"zone": "left"
}, {
"time": 196974,
"type": "tap",
"zone": "right"
}, {
"time": 197299,
"type": "tap",
"zone": "left"
}, {
"time": 198600,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 198600,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 199900,
"type": "tap",
"zone": "left"
}, {
"time": 201200,
"type": "tap",
"zone": "left"
}, {
"time": 201851,
"type": "tap",
"zone": "left"
}, {
"time": 202524,
"type": "tap",
"zone": "left"
}, {
"time": 203174,
"type": "tap",
"zone": "left"
}, {
"time": 203499,
"type": "tap",
"zone": "right"
}, {
"time": 203824,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 203824,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 205125,
"type": "tap",
"zone": "left"
}, {
"time": 205775,
"type": "tap",
"zone": "left"
}, {
"time": 206425,
"type": "tap",
"zone": "left"
}, {
"time": 207075,
"type": "tap",
"zone": "left"
}, {
"time": 207725,
"type": "hold",
"zone": "left",
"duration": 1000
}, {
"time": 208724,
"type": "tap",
"zone": "right"
}, {
"time": 209049,
"type": "tap",
"zone": "left",
"color": 16753920
}, {
"time": 209049,
"type": "tap",
"zone": "right",
"color": 16753920
}, {
"time": 209699,
"type": "tap",
"zone": "left"
}, {
"time": 210349,
"type": "tap",
"zone": "left"
}, {
"time": 211649,
"type": "tap",
"zone": "left"
}, {
"time": 212950,
"type": "tap",
"zone": "left"
}, {
"time": 213600,
"type": "tap",
"zone": "left"
}, {
"time": 214250,
"type": "tap",
"zone": "left"
}, {
"time": 214900,
"type": "tap",
"zone": "left"
}, {
"time": 215550,
"type": "hold",
"zone": "right",
"duration": 1000
}, {
"time": 216874,
"type": "tap",
"zone": "left"
}, {
"time": 217524,
"type": "tap",
"zone": "left"
}, {
"time": 217849,
"type": "tap",
"zone": "right"
}, {
"time": 218174,
"type": "tap",
"zone": "left"
}, {
"time": 218499,
"type": "tap",
"zone": "right"
}, {
"time": 218824,
"type": "tap",
"zone": "left"
}, {
"time": 219428,
"type": "tap",
"zone": "left"
}, {
"time": 220636,
"type": "tap",
"zone": "left"
}, {
"time": 221936,
"type": "tap",
"zone": "left"
}, {
"time": 223236,
"type": "tap",
"zone": "left"
}, {
"time": 224536,
"type": "tap",
"zone": "left"
}, {
"time": 225837,
"type": "tap",
"zone": "left"
}, {
"time": 227137,
"type": "tap",
"zone": "left"
}]
}
};
function getSongLength(songName) {
var song = SongDatabase[songName];
if (!song) {
return "0:00";
}
var totalSeconds = Math.floor(song.totalLength / 1000);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
return minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
}
function updateHighScore(songName, score, grade) {
var updated = false;
if (SongDatabase[songName]) {
if (score > SongDatabase[songName].highScore) {
SongDatabase[songName].highScore = score;
// Save high scores to storage
if (!storage.highScores) {
storage.highScores = {};
}
storage.highScores[songName] = score;
updated = true;
}
// Update grade if it's better (S > A > B > C)
var currentGrade = SongDatabase[songName].highestGrade || 'C';
var gradeOrder = ['C', 'B', 'A', 'S'];
if (gradeOrder.indexOf(grade) > gradeOrder.indexOf(currentGrade)) {
SongDatabase[songName].highestGrade = grade;
// Save highest grades to storage
if (!storage.highestGrades) {
storage.highestGrades = {};
}
storage.highestGrades[songName] = grade;
updated = true;
}
}
return updated;
}
function calculateNotePosition(noteTime) {
var cycleTime = noteTime % SCANNER_CYCLE_DURATION;
var y;
if (cycleTime < SCANNER_HALF_CYCLE) {
var progress = cycleTime / SCANNER_HALF_CYCLE;
y = SCANNER_Y_MAX - progress * PLAY_AREA_HEIGHT;
} else {
var progress = (cycleTime - SCANNER_HALF_CYCLE) / SCANNER_HALF_CYCLE;
y = SCANNER_Y_MIN + progress * PLAY_AREA_HEIGHT;
}
return y;
}
function calculateZoneX(zone, noteY) {
var sideMargin = GAME_WIDTH * SAFETY_MARGIN_SIDES_PERCENT;
var randomSpan = GAME_WIDTH / 2 - 2 * sideMargin;
if (randomSpan < 0) {
randomSpan = 0;
}
// Check if note is in the bottom 15% of the play area
var isInBottomArea = noteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15;
var xPosition;
if (zone === 'left') {
xPosition = sideMargin;
if (isInBottomArea) {
// In bottom area, favor the right side of the left zone (more central)
// Use the right 60% of the left zone's available space
var centralSpan = randomSpan * 0.6;
var offset = randomSpan * 0.4; // Skip the leftmost 40%
xPosition += offset + Math.random() * centralSpan;
} else {
// Normal positioning for upper areas
xPosition += Math.random() * randomSpan;
}
} else {
xPosition = GAME_WIDTH / 2 + sideMargin;
if (isInBottomArea) {
// In bottom area, favor the left side of the right zone (more central)
// Use the left 60% of the right zone's available space
var centralSpan = randomSpan * 0.6;
xPosition += Math.random() * centralSpan;
} else {
// Normal positioning for upper areas
xPosition += Math.random() * randomSpan;
}
}
return xPosition;
}
function createZoneIndicators() {
leftZone = game.attachAsset('zoneIndicator', {
anchorX: 0,
anchorY: 0
});
leftZone.x = 0;
leftZone.y = 0;
leftZone.width = GAME_WIDTH / 2;
leftZone.alpha = 0.1;
leftZone.tint = 0x00ffff;
rightZone = game.attachAsset('zoneIndicator', {
anchorX: 0,
anchorY: 0
});
rightZone.x = GAME_WIDTH / 2;
rightZone.y = 0;
rightZone.width = GAME_WIDTH / 2;
rightZone.alpha = 0.1;
rightZone.tint = 0xff00ff;
}
function showAccuracyPopup(text, x, y, color) {
var popup = new Text2(text, {
size: ACCURACY_POPUP_FONT_SIZE,
fill: color,
stroke: 0x000000,
strokeThickness: 4
});
popup.anchor.set(0.5, 0.5);
popup.x = x;
popup.y = y + ACCURACY_POPUP_Y_OFFSET;
popup.alpha = 0;
game.addChild(popup);
var initialY = popup.y;
tween(popup, {
alpha: 1,
y: initialY - 30
}, {
duration: ACCURACY_POPUP_DURATION * 0.25,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(popup, {
alpha: 0,
y: initialY - 130
}, {
duration: ACCURACY_POPUP_DURATION * 0.75,
easing: tween.easeIn,
onFinish: function onFinish() {
if (popup && popup.destroy) {
popup.destroy();
}
}
});
}
});
}
function initializeTitleScreen() {
titleScreen.container = new Container();
game.addChild(titleScreen.container);
titleScreen.title = titleScreen.container.attachAsset('titleImage', {
anchorX: 0.5,
anchorY: 0.5
});
titleScreen.title.x = GAME_WIDTH / 2;
titleScreen.title.y = GAME_HEIGHT / 2 - 200;
titleScreen.subtitle = new Text2('Press to Start', {
size: 100,
fill: 0xCCCCCC
});
titleScreen.subtitle.anchor.set(0.5, 0.5);
titleScreen.subtitle.x = GAME_WIDTH / 2;
titleScreen.subtitle.y = GAME_HEIGHT / 2 + 100 + GAME_HEIGHT * 0.15;
titleScreen.container.addChild(titleScreen.subtitle);
var pulseScale = 1;
var pulseDirection = 1;
titleScreen.pulseInterval = LK.setInterval(function () {
pulseScale += pulseDirection * 0.02;
if (pulseScale > 1.1) {
pulseScale = 1.1;
pulseDirection = -1;
} else if (pulseScale < 0.9) {
pulseScale = 0.9;
pulseDirection = 1;
}
titleScreen.subtitle.scale.set(pulseScale);
}, 50);
}
function initializeSongSelectScreen() {
songSelectScreen.container = new Container();
game.addChild(songSelectScreen.container);
songSelectScreen.container.visible = false;
songSelectScreen.title = new Text2('Select Song', {
size: 150,
fill: 0xFFFFFF
});
songSelectScreen.title.anchor.set(0.5, 0);
songSelectScreen.title.x = GAME_WIDTH / 2;
songSelectScreen.title.y = 100;
songSelectScreen.container.addChild(songSelectScreen.title);
songSelectScreen.scrollContainer = new Container();
songSelectScreen.scrollContainer.x = 0;
songSelectScreen.scrollContainer.y = 300;
songSelectScreen.container.addChild(songSelectScreen.scrollContainer);
createSongItems();
updateScrollLimits();
songSelectScreen.tutorialButton = new Text2('How to Play', {
size: 90,
fill: 0xCCCCCC
});
songSelectScreen.tutorialButton.anchor.set(0.5, 1);
songSelectScreen.tutorialButton.x = GAME_WIDTH / 2;
songSelectScreen.tutorialButton.y = GAME_HEIGHT - 60; // Positioned at the bottom
songSelectScreen.container.addChild(songSelectScreen.tutorialButton);
}
function createSongItems() {
songSelectScreen.items = [];
var songNames = Object.keys(SongDatabase);
for (var i = 0; i < songNames.length; i++) {
var songName = songNames[i];
var song = SongDatabase[songName];
var yPos = i * (SONG_ITEM_HEIGHT + 50);
var itemContainer = new Container();
itemContainer.x = 100;
itemContainer.y = yPos;
itemContainer.songName = songName;
var bg = game.attachAsset('zoneIndicator', {
anchorX: 0,
anchorY: 0
});
bg.width = GAME_WIDTH - 200;
bg.height = SONG_ITEM_HEIGHT;
bg.alpha = 0.2;
bg.tint = 0x333333;
itemContainer.addChild(bg);
var displayName = songName;
if (songName === 'Runner') {
displayName = songName + ' (Hard)';
}
var titleText = new Text2(displayName, {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0); // Center horizontally, align top vertically
titleText.x = (GAME_WIDTH - 200) / 2; // Center within the background width
titleText.y = 30;
itemContainer.addChild(titleText);
// Define vertical padding between text elements
var verticalPadding = 20;
var infoText = new Text2('BPM: ' + song.bpm + ' | Length: ' + getSongLength(songName), {
size: 62.5,
fill: 0xCCCCCC
});
infoText.anchor.set(0.5, 0); // Center horizontally, align top vertically
infoText.x = (GAME_WIDTH - 200) / 2; // Center within the background width
// Position infoText below titleText (assuming titleText font size 100 is approx height)
infoText.y = titleText.y + 100 + verticalPadding;
itemContainer.addChild(infoText);
var gradeDisplay = song.highestGrade || 'C';
var scoreText = new Text2('High Score: ' + song.highScore + ' | Grade: ' + gradeDisplay, {
size: 62.5,
fill: 0xFFDD00
});
scoreText.anchor.set(0.5, 0); // Center horizontally, align top vertically
scoreText.x = (GAME_WIDTH - 200) / 2; // Center within the background width
// Position scoreText below infoText (assuming infoText font size 62.5 is approx height)
scoreText.y = infoText.y + 62.5 + verticalPadding;
itemContainer.addChild(scoreText);
songSelectScreen.scrollContainer.addChild(itemContainer);
songSelectScreen.items.push(itemContainer);
}
}
function updateScrollLimits() {
var totalHeight = songSelectScreen.items.length * (SONG_ITEM_HEIGHT + 50);
var containerHeight = GAME_HEIGHT - 400;
maxScrollY = Math.max(0, totalHeight - containerHeight);
}
function showTitleScreen() {
currentGameState = GAME_STATE_TITLE;
if (titleScreen.container) {
titleScreen.container.visible = true;
}
if (songSelectScreen.container) {
songSelectScreen.container.visible = false;
}
if (resultsScreen.container) {
resultsScreen.container.visible = false;
}
if (tutorialScreen.container) {
tutorialScreen.container.visible = false;
}
hideGameplayUI();
}
function showSongSelectScreen() {
currentGameState = GAME_STATE_SONG_SELECT;
if (titleScreen.container) {
titleScreen.container.visible = false;
}
if (songSelectScreen.container) {
songSelectScreen.container.visible = true;
}
if (resultsScreen.container) {
resultsScreen.container.visible = false;
}
if (tutorialScreen.container) {
tutorialScreen.container.visible = false;
}
songSelectionInProgress = false; // Reset flag when song select screen is shown
hideGameplayUI();
songSelectScrollY = 0;
songSelectScreen.scrollContainer.y = 300;
}
function hideGameplayUI() {
if (scoreTxt) {
scoreTxt.visible = false;
}
// Zone indicators removed - no longer hiding background zones
// if (leftZone) {
// leftZone.visible = false;
// }
// if (rightZone) {
// rightZone.visible = false;
// }
if (scanner) {
scanner.visible = false;
}
// Also ensure tutorial is hidden if gameplay UI is hidden for other reasons
if (tutorialScreen.container && currentGameState !== GAME_STATE_TUTORIAL) {
tutorialScreen.container.visible = false;
}
}
function showGameplayUI() {
if (scoreTxt) {
scoreTxt.visible = true;
}
// Zone indicators removed - no longer showing background zones
// if (leftZone) {
// leftZone.visible = true;
// }
// if (rightZone) {
// rightZone.visible = true;
// }
if (scanner) {
scanner.visible = true;
}
if (titleScreen.container) {
titleScreen.container.visible = false;
}
if (songSelectScreen.container) {
songSelectScreen.container.visible = false;
}
if (tutorialScreen.container) {
tutorialScreen.container.visible = false;
}
if (resultsScreen.container) {
resultsScreen.container.visible = false;
}
}
function startCountdown() {
countdownTxt = new Text2('3', {
size: 200,
fill: 0xFFFFFF
});
countdownTxt.anchor.set(0.5, 0.5);
countdownTxt.x = GAME_WIDTH / 2;
countdownTxt.y = GAME_HEIGHT / 2;
game.addChild(countdownTxt);
// Start countdown acceleration
isCountdownActive = true;
countdownAcceleration = 1;
var startTime = Date.now();
var countdownDuration = 4000; // 4 seconds total
var count = 3;
var countdownInterval = LK.setInterval(function () {
count--;
if (count > 0) {
countdownTxt.setText(count.toString());
} else if (count === 0) {
countdownTxt.setText('GO!');
} else {
LK.clearInterval(countdownInterval);
if (countdownTxt && countdownTxt.destroy) {
countdownTxt.destroy();
}
// Keep the accelerated speed when game starts
baseParticleSpeed = 5; // Maintain 5x speed
isCountdownActive = false;
countdownAcceleration = 1;
startGame();
}
}, 1000);
// Smooth acceleration during countdown
var accelerationInterval = LK.setInterval(function () {
if (!isCountdownActive) {
LK.clearInterval(accelerationInterval);
return;
}
var elapsed = Date.now() - startTime;
var progress = Math.min(elapsed / countdownDuration, 1);
// Smooth acceleration curve - starts slow, ramps up faster
var easedProgress = progress * progress;
countdownAcceleration = 1 + easedProgress * 4; // 1x to 5x speed
}, 50); // Update every 50ms for smooth acceleration
}
function showResultsScreen() {
currentGameState = GAME_STATE_RESULTS;
if (titleScreen.container) {
titleScreen.container.visible = false;
}
if (songSelectScreen.container) {
songSelectScreen.container.visible = false;
}
if (resultsScreen.container) {
resultsScreen.container.visible = true;
}
if (tutorialScreen.container) {
tutorialScreen.container.visible = false;
}
hideGameplayUI();
}
function startGame() {
currentGameState = GAME_STATE_PLAYING;
songSelectionInProgress = false; // Selection process is complete, game is starting
gameStarted = true;
gameStartTime = Date.now();
showGameplayUI();
moveScanner();
// Determine which music to play based on the selected song
var musicId = 'gameMusic'; // Default music
if (currentSongData === SongDatabase['Remember This']) {
musicId = 'rememberThis';
} else if (currentSongData === SongDatabase['Arcade Heart']) {
musicId = 'arcadeHeart';
} else if (currentSongData === SongDatabase['Runner']) {
musicId = 'runner';
}
LK.playMusic(musicId, {
loop: false
});
}
function moveScanner() {
if (!scanner || !gameStarted || currentGameState !== GAME_STATE_PLAYING) {
return;
}
tween.stop(scanner);
var targetY = scannerIsMovingUp ? SCANNER_Y_MIN : SCANNER_Y_MAX;
tween(scanner, {
y: targetY
}, {
duration: SCANNER_CYCLE_DURATION / 2,
easing: tween.linear,
onFinish: function onFinish() {
scannerIsMovingUp = !scannerIsMovingUp;
moveScanner();
}
});
}
function spawnNotesForCurrentTime(currentTime) {
if (!currentSongData) {
return;
}
currentSongData.notes.forEach(function (noteData, index) {
var noteKey = index + '_' + noteData.time;
var alreadySpawned = false;
for (var i = 0; i < spawnedNotes.length; i++) {
if (spawnedNotes[i] === noteKey) {
alreadySpawned = true;
break;
}
}
if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) {
var calculatedY = calculateNotePosition(noteData.time);
var calculatedX = calculateZoneX(noteData.zone, calculatedY); // Pass Y position
var processedNoteData = {
time: noteData.time,
type: noteData.type,
x: calculatedX,
y: calculatedY,
color: noteData.color,
duration: noteData.duration
};
var note = new Note(processedNoteData, currentTime);
notes.push(note);
game.addChild(note);
note.spawnIn();
spawnedNotes.push(noteKey);
}
});
}
function setupGame(songName) {
LK.setScore(0);
gameStarted = false;
perfectCount = 0;
goodCount = 0;
missCount = 0;
// Reset particle speed for new game
baseParticleSpeed = 1;
isCountdownActive = false;
countdownAcceleration = 1;
// Reset particle direction to downward
globalParticleDirection = {
x: 0,
y: 1
};
targetParticleDirection = {
x: 0,
y: 1
};
isChangingDirection = false;
lastDirectionChangeTime = Date.now();
nextDirectionChangeDelay = 15000 + Math.random() * 15000;
if (scoreTxt && scoreTxt.destroy) {
scoreTxt.destroy();
}
scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(LK.getScore());
// createZoneIndicators(); // Removed zone indicators from background
if (scanner && scanner.destroy) {
scanner.destroy();
}
scanner = new Scanner();
scanner.y = SCANNER_Y_MAX;
game.addChild(scanner);
notes.forEach(function (note) {
if (note && note.destroy) {
note.destroy();
}
});
notes = [];
spawnedNotes = [];
currentSongData = SongDatabase[songName];
BPM = currentSongData.bpm;
BEAT_DURATION_MS = 60 / BPM * 1000;
SCANNER_CYCLE_DURATION = currentSongData.scannerCycleDuration;
SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2;
scannerIsMovingUp = true;
startCountdown();
// Reinitialize particles for proper distribution
if (backgroundParticles.length === 0) {
for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) {
var particle = new BackgroundParticle();
// Let init() handle the positioning
backgroundParticles.push(particle);
game.addChildAt(particle, 0);
}
} else {
// Reinitialize existing particles for new distribution
backgroundParticles.forEach(function (p) {
p.init(); // init now handles random x and y
});
}
}
function checkZoneHit(x, y) {
var hitZone = x < GAME_WIDTH / 2 ? 'left' : 'right';
var currentTime = Date.now() - gameStartTime;
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (!note || !note.active || note.isHit || note.isSpawning || !note.noteGraphics) {
continue;
}
if (note.zone === hitZone) {
var noteCoreHeight = note.noteGraphics.height;
var noteCenterY = note.y;
var scannerY = scanner.y;
var perfectHitRadius = noteCoreHeight * PERFECT_HIT_RATIO / 2;
var goodHitRadius = noteCoreHeight / 2;
var perfectHitTop = noteCenterY - perfectHitRadius;
var perfectHitBottom = noteCenterY + perfectHitRadius;
var goodHitTop = noteCenterY - goodHitRadius;
var goodHitBottom = noteCenterY + goodHitRadius;
var accuracyText = null;
var hitPoints = 0;
var popupColor = 0xFFFFFF;
if (scannerY >= perfectHitTop && scannerY <= perfectHitBottom) {
accuracyText = "PERFECT";
popupColor = PERFECT_POPUP_COLOR;
if (note.type === 'tap') {
hitPoints = POINTS_PERFECT_TAP;
} else if (note.type === 'hold') {
hitPoints = POINTS_PERFECT_HOLD_START;
}
} else if (scannerY >= goodHitTop && scannerY <= goodHitBottom) {
accuracyText = "GOOD";
popupColor = GOOD_POPUP_COLOR;
if (note.type === 'tap') {
hitPoints = POINTS_GOOD_TAP;
} else if (note.type === 'hold') {
hitPoints = POINTS_GOOD_HOLD_START;
}
}
if (accuracyText) {
note.isHit = true;
showAccuracyPopup(accuracyText, note.x, note.y, popupColor);
if (accuracyText === "PERFECT") {
perfectCount++;
} else if (accuracyText === "GOOD") {
goodCount++;
}
if (note.type === 'tap') {
note.showHitEffect();
} else if (note.type === 'hold') {
note.startHoldEffect();
}
if (hitPoints > 0) {
LK.setScore(LK.getScore() + hitPoints);
scoreTxt.setText(LK.getScore());
}
break;
}
}
}
}
function initializeTutorialScreen() {
tutorialScreen.container = new Container();
tutorialScreen.container.visible = false;
game.addChild(tutorialScreen.container);
var bg = tutorialScreen.container.attachAsset('tutorialBg', {
anchorX: 0,
anchorY: 0
});
bg.alpha = 0.9;
bg.width = GAME_WIDTH;
bg.height = GAME_HEIGHT;
var currentY = 80;
var smallGap = 20;
var mediumGap = 40;
var largeGap = 60;
var textBlockGap = 10;
var visualOffsetY = 30; // Offset Y for visuals relative to text center
var visualScale = 0.3;
var noteVisualXLeft = GAME_WIDTH / 2 - 150;
var noteVisualXMid = GAME_WIDTH / 2;
var noteVisualXRight = GAME_WIDTH / 2 + 150;
// Title
var title = _createTutorialText("How to Play", 120, currentY, tutorialScreen.container, 0x00FFFF, 'center');
currentY += title.height + largeGap;
// Introduction
var introText = _createTutorialText("The scanner line moves up and down the screen. Notes will appear on the screen; interact with them as the scanner aligns with each note!", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'center');
currentY += introText.height + mediumGap;
// Zones & Controls
var zonesHeader = _createTutorialText("Play Zones & Controls", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center');
currentY += zonesHeader.height + smallGap;
var zonesText = _createTutorialText("The screen has a LEFT zone (Blue) and a RIGHT zone (Red). Tap ANYWHERE within the correct zone when a note aligns with the scanner line. You do NOT need to tap the note itself.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += zonesText.height + mediumGap;
// Tap Notes
var tapHeader = _createTutorialText("Tap Notes", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center');
currentY += tapHeader.height + smallGap;
var tapDesc = _createTutorialText("Tap the zone once when the note aligns with the scanner.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += tapDesc.height + textBlockGap;
var regularTapHeader = _createTutorialText("- Regular Tap Notes:", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += regularTapHeader.height + textBlockGap;
var leftTapText = _createTutorialText(" Left Zone (Blue): Tap LEFT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
_createTutorialNoteVisual('noteCore', COLOR_LEFT_ZONE, noteVisualXLeft, currentY + visualOffsetY, visualScale, tutorialScreen.container);
currentY += leftTapText.height + textBlockGap;
var rightTapText = _createTutorialText(" Right Zone (Red): Tap RIGHT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
_createTutorialNoteVisual('noteCore', COLOR_RIGHT_ZONE, noteVisualXRight, currentY + visualOffsetY, visualScale, tutorialScreen.container);
currentY += rightTapText.height + mediumGap;
var syncTapHeader = _createTutorialText("- Synced Tap Notes (Pink):", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += syncTapHeader.height + textBlockGap;
var syncTapText = _createTutorialText(" Appear on both sides. Tap BOTH zones simultaneously.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
currentY += syncTapText.height + mediumGap; // Space after text, before visuals
var scaledVisualHeight = 300 * visualScale; // Assuming noteCore asset height is 300px
var visualCenterYForTap = currentY + scaledVisualHeight / 2;
_createTutorialNoteVisual('noteCore', COLOR_SYNC_TAP, noteVisualXLeft, visualCenterYForTap, visualScale, tutorialScreen.container);
_createTutorialNoteVisual('noteCore', COLOR_SYNC_TAP, noteVisualXRight, visualCenterYForTap, visualScale, tutorialScreen.container);
currentY += scaledVisualHeight + largeGap; // Space after visuals line
// Hold Notes
var holdHeader = _createTutorialText("Hold Notes", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center');
currentY += holdHeader.height + smallGap;
var holdDesc = _createTutorialText("Tap and HOLD for the note's trail duration. Release as the trail ends.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += holdDesc.height + textBlockGap;
var regularHoldHeader = _createTutorialText("- Regular Hold Notes:", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += regularHoldHeader.height + textBlockGap;
var leftHoldText = _createTutorialText(" Left Zone (Blue): Hold LEFT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
_createTutorialNoteVisual('holdNoteCore', COLOR_LEFT_ZONE, noteVisualXLeft, currentY + visualOffsetY, visualScale, tutorialScreen.container);
currentY += leftHoldText.height + textBlockGap;
var rightHoldText = _createTutorialText(" Right Zone (Red): Hold RIGHT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
_createTutorialNoteVisual('holdNoteCore', COLOR_RIGHT_ZONE, noteVisualXRight, currentY + visualOffsetY, visualScale, tutorialScreen.container);
currentY += rightHoldText.height + mediumGap;
var syncHoldHeader = _createTutorialText("- Synced Hold Notes (Purple):", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left');
currentY += syncHoldHeader.height + textBlockGap;
var syncHoldText = _createTutorialText(" Appear on both sides. Hold BOTH zones simultaneously.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150);
currentY += syncHoldText.height + mediumGap; // Space after text, before visuals
var scaledVisualHeight = 300 * visualScale; // Assuming holdNoteCore asset height is 300px
var visualCenterYForHold = currentY + scaledVisualHeight / 2;
_createTutorialNoteVisual('holdNoteCore', COLOR_SYNC_HOLD, noteVisualXLeft, visualCenterYForHold, visualScale, tutorialScreen.container);
_createTutorialNoteVisual('holdNoteCore', COLOR_SYNC_HOLD, noteVisualXRight, visualCenterYForHold, visualScale, tutorialScreen.container);
currentY += scaledVisualHeight + largeGap; // Space after visuals line
// Close Button
// Position close button near the bottom, ensure currentY doesn't push it off screen
var closeButtonY = Math.max(currentY + mediumGap, GAME_HEIGHT - 150);
tutorialScreen.closeButton = _createTutorialText("Tap to Close", 70, closeButtonY, tutorialScreen.container, 0xCCCCCC, 'center');
tutorialScreen.closeButton.interactive = true; // For clarity, though handled by game.down
}
function handleSongItemClick(x, y) {
if (songSelectionInProgress) {
return true; // Prevent new selections while one is processing
}
for (var i = 0; i < songSelectScreen.items.length; i++) {
var item = songSelectScreen.items[i];
// Ensure item and item.scale exist before trying to use them for tweening
if (!item || !item.scale) {
continue;
}
var itemX = item.x;
var itemY = item.y + songSelectScreen.scrollContainer.y;
// Check if the click is within the bounds of the current item
if (x >= itemX && x <= itemX + (GAME_WIDTH - 200) && y >= itemY && y <= itemY + SONG_ITEM_HEIGHT) {
songSelectionInProgress = true; // Set flag
// Stop any existing scale tweens on this item and reset scale before pulsing
tween.stop(item.scale);
item.scale.set(1.0, 1.0);
// Pulse animation: scale down (shrink)
tween(item.scale, {
x: 0.9,
y: 0.9
}, {
duration: 100,
// Duration of scale down
easing: tween.easeOut,
onFinish: function onFinish() {
// Pulse animation: scale back to normal
tween(item.scale, {
x: 1.0,
y: 1.0
}, {
duration: 100,
// Duration of scale back to normal
easing: tween.easeIn,
onFinish: function onFinish() {
// Short timeout to allow pulse to be visible before screen change
LK.setTimeout(function () {
if (songSelectScreen.container) {
songSelectScreen.container.visible = false;
}
setupGame(item.songName);
// songSelectionInProgress will be reset by startGame or other screen transitions
}, 150); // Delay before starting game setup
}
});
}
});
return true; // Song clicked and processing started
}
}
return false; // No song item was clicked
}
function initializeResultsScreen() {
resultsScreen.container = new Container();
game.addChild(resultsScreen.container);
resultsScreen.container.visible = false;
resultsScreen.title = new Text2('Results', {
size: 180,
fill: 0xFFFFFF,
stroke: 0x00FFFF,
strokeThickness: 6
});
resultsScreen.title.anchor.set(0.5, 0.5);
resultsScreen.title.x = GAME_WIDTH / 2;
resultsScreen.title.y = GAME_HEIGHT / 2 - 350;
resultsScreen.container.addChild(resultsScreen.title);
resultsScreen.scoreText = new Text2('Final Score: 0', {
size: 100,
fill: 0xFFFFFF
});
resultsScreen.scoreText.anchor.set(0.5, 0.5);
resultsScreen.scoreText.x = GAME_WIDTH / 2;
resultsScreen.scoreText.y = GAME_HEIGHT / 2 - 150;
resultsScreen.container.addChild(resultsScreen.scoreText);
resultsScreen.highScoreText = new Text2('High Score: 0', {
size: 80,
fill: 0xFFDD00
});
resultsScreen.highScoreText.anchor.set(0.5, 0.5);
resultsScreen.highScoreText.x = GAME_WIDTH / 2;
resultsScreen.highScoreText.y = GAME_HEIGHT / 2 - 50;
resultsScreen.container.addChild(resultsScreen.highScoreText);
resultsScreen.perfectText = new Text2('Perfect: 0', {
size: 70,
fill: 0x00FF00
});
resultsScreen.perfectText.anchor.set(0.5, 0.5);
resultsScreen.perfectText.x = GAME_WIDTH / 2;
resultsScreen.perfectText.y = GAME_HEIGHT / 2 + 50;
resultsScreen.container.addChild(resultsScreen.perfectText);
resultsScreen.goodText = new Text2('Good: 0', {
size: 70,
fill: 0xFFFF00
});
resultsScreen.goodText.anchor.set(0.5, 0.5);
resultsScreen.goodText.x = GAME_WIDTH / 2;
resultsScreen.goodText.y = GAME_HEIGHT / 2 + 130;
resultsScreen.container.addChild(resultsScreen.goodText);
resultsScreen.missText = new Text2('Miss: 0', {
size: 70,
fill: 0xFF0000
});
resultsScreen.missText.anchor.set(0.5, 0.5);
resultsScreen.missText.x = GAME_WIDTH / 2;
resultsScreen.missText.y = GAME_HEIGHT / 2 + 210;
resultsScreen.container.addChild(resultsScreen.missText);
resultsScreen.continueText = new Text2('Tap to Continue', {
size: 90,
fill: 0xCCCCCC
});
resultsScreen.continueText.anchor.set(0.5, 0.5);
resultsScreen.continueText.x = GAME_WIDTH / 2;
resultsScreen.continueText.y = GAME_HEIGHT / 2 + 430;
resultsScreen.container.addChild(resultsScreen.continueText);
if (resultsScreen.pulseInterval) {
LK.clearInterval(resultsScreen.pulseInterval);
}
resultsScreen.pulseInterval = LK.setInterval(function () {
if (currentGameState === GAME_STATE_RESULTS && resultsScreen.continueText) {
var pulseScale = resultsScreen.continueText.scale.x;
var pulseDirection = resultsScreen.continueText.pulseDirection || 1;
pulseScale += pulseDirection * 0.02;
if (pulseScale > 1.1) {
pulseScale = 1.1;
pulseDirection = -1;
} else if (pulseScale < 0.9) {
pulseScale = 0.9;
pulseDirection = 1;
}
resultsScreen.continueText.scale.set(pulseScale);
resultsScreen.continueText.pulseDirection = pulseDirection;
}
}, 50);
}
function showTutorialScreen() {
currentGameState = GAME_STATE_TUTORIAL;
if (titleScreen.container) {
titleScreen.container.visible = false;
}
if (songSelectScreen.container) {
songSelectScreen.container.visible = false;
}
if (resultsScreen.container) {
resultsScreen.container.visible = false;
}
if (tutorialScreen.container) {
tutorialScreen.container.visible = true;
}
hideGameplayUI();
}
initializeTitleScreen();
initializeSongSelectScreen();
initializeResultsScreen();
initializeTutorialScreen(); // Initialize the new tutorial screen
for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) {
var particle = new BackgroundParticle();
particle.y = Math.random() * GAME_HEIGHT;
backgroundParticles.push(particle);
game.addChildAt(particle, 0);
}
showTitleScreen();
game.down = function (x, y, obj) {
if (currentGameState === GAME_STATE_TITLE) {
showSongSelectScreen();
return;
}
if (currentGameState === GAME_STATE_TUTORIAL) {
// Check click on tutorial's close button (entire screen acts as close for simplicity)
// More precise check if tutorialScreen.closeButton was an actual button object with bounds:
// var cb = tutorialScreen.closeButton;
// if (x >= cb.x - cb.width * cb.anchor.x && x <= cb.x + cb.width * (1-cb.anchor.x) &&
// y >= cb.y - cb.height * cb.anchor.y && y <= cb.y + cb.height * (1-cb.anchor.y)) {
showSongSelectScreen();
// }
return;
}
if (currentGameState === GAME_STATE_RESULTS) {
showSongSelectScreen();
return;
}
if (currentGameState === GAME_STATE_SONG_SELECT) {
// Check for tutorial button click
var tb = songSelectScreen.tutorialButton;
if (tb && x >= tb.x - tb.width * tb.anchor.x && x <= tb.x + tb.width * (1 - tb.anchor.x) && y >= tb.y - tb.height && y <= tb.y) {
// anchor is 0.5, 1
showTutorialScreen();
return;
}
if (handleSongItemClick(x, y)) {
return;
}
isDraggingSongList = true;
dragStartYSongList = y;
return;
}
if (currentGameState === GAME_STATE_PLAYING && gameStarted) {
checkZoneHit(x, y);
var hitZoneForDown = x < GAME_WIDTH / 2 ? 'left' : 'right';
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (!note.active || note.isSpawning || note.holdSuccessfullyCompleted || note.isMissed) {
continue;
}
if (note.zone === hitZoneForDown && note.isHit) {
if (note.type === 'hold' && !note.isHolding) {
note.isHolding = true;
activeHoldNotes[note.zone] = note;
}
}
}
isDragging = true;
dragStartX = x;
dragStartY = y;
}
};
game.move = function (x, y, obj) {
if (currentGameState === GAME_STATE_SONG_SELECT && isDraggingSongList) {
var deltaY = y - dragStartYSongList;
songSelectScrollY = Math.max(0, Math.min(maxScrollY, songSelectScrollY - deltaY));
songSelectScreen.scrollContainer.y = 300 - songSelectScrollY;
dragStartYSongList = y;
}
if (currentGameState === GAME_STATE_PLAYING && isDragging && gameStarted) {}
};
game.up = function (x, y, obj) {
if (currentGameState === GAME_STATE_SONG_SELECT) {
isDraggingSongList = false;
return;
}
if (currentGameState === GAME_STATE_PLAYING && gameStarted) {
var releasedZone = x < GAME_WIDTH / 2 ? 'left' : 'right';
var heldNote = activeHoldNotes[releasedZone];
if (heldNote && heldNote.isHolding) {
heldNote.isHolding = false;
delete activeHoldNotes[releasedZone];
var currentTimeMs = Date.now() - gameStartTime;
var noteExpectedEndTimeMs = heldNote.hitTime + heldNote.duration;
var absoluteEndNoteY = heldNote.y + (heldNote.endNote ? heldNote.endNote.y : 0);
var hitTimeCycle = heldNote.hitTime % SCANNER_CYCLE_DURATION;
var noteScannerMovingUp = hitTimeCycle < SCANNER_HALF_CYCLE;
var trailDirection = noteScannerMovingUp ? -1 : 1;
var scannerAtEnd;
if (trailDirection === -1) {
scannerAtEnd = scanner.y <= absoluteEndNoteY + HIT_TOLERANCE_PX_FOR_HOLD_END && scanner.y >= absoluteEndNoteY - HIT_TOLERANCE_PX_FOR_HOLD_END / 2;
} else {
scannerAtEnd = scanner.y >= absoluteEndNoteY - HIT_TOLERANCE_PX_FOR_HOLD_END && scanner.y <= absoluteEndNoteY + HIT_TOLERANCE_PX_FOR_HOLD_END / 2;
}
var timeReachedOrPassedEnd = currentTimeMs >= noteExpectedEndTimeMs - HOLD_END_GRACE_PERIOD_MS;
var timeNotTooLate = currentTimeMs <= noteExpectedEndTimeMs + HOLD_END_GRACE_PERIOD_MS + 200;
if (scannerAtEnd && timeReachedOrPassedEnd && timeNotTooLate) {
heldNote.completeHold();
LK.setScore(LK.getScore() + 50);
scoreTxt.setText(LK.getScore());
} else {
heldNote.failHold();
missCount++;
}
}
isDragging = false;
}
};
game.update = function () {
// Update background particles with new space travel effects
backgroundParticles.forEach(function (particle) {
particle.updateParticle();
});
// Handle direction changes during gameplay
if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) {
var currentTimeForDirectionChange = Date.now();
if (currentTimeForDirectionChange - lastDirectionChangeTime > nextDirectionChangeDelay) {
changeParticleDirection();
}
}
// Handle global particle direction changes (original code)
var currentTime = Date.now();
// This part is now handled by the block above for GAME_STATE_PLAYING
// and the general update below might be redundant or could be simplified if only for non-playing states
// For now, let's keep the existing logic structure for non-playing state direction changes or initial setup
if (!isChangingDirection && currentGameState !== GAME_STATE_PLAYING && currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) {
changeParticleDirection(); // Call the unified function
}
// The following block for `if (isChangingDirection)` is now handled by `getCurrentParticleDirection`
// which is called by `particle.updateParticle()`.
// So, the direct manipulation of `globalParticleDirection` here might be redundant
// if particles are always updating based on `getCurrentParticleDirection`.
// However, `changeParticleDirection` sets `isChangingDirection = true` and `directionChangeStartTime`.
// `getCurrentParticleDirection` uses these to interpolate and then resets `isChangingDirection`.
// The logic seems okay as `getCurrentParticleDirection` will handle the smooth transition.
if (currentGameState !== GAME_STATE_PLAYING || !gameStarted) {
return;
}
var currentTime = Date.now() - gameStartTime;
spawnNotesForCurrentTime(currentTime);
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (!note) {
notes.splice(i, 1);
continue;
}
if (note.type === 'hold' && note.isHolding && note.active && !note.holdSuccessfullyCompleted && !note.isMissed) {
note.updateHoldEffects(scanner.y);
var currentTimeMsForUpdate = Date.now() - gameStartTime;
var noteExpectedEndTimeMsForUpdate = note.hitTime + note.duration;
if (currentTimeMsForUpdate > noteExpectedEndTimeMsForUpdate + HOLD_END_GRACE_PERIOD_MS + 500) {
if (activeHoldNotes[note.zone] === note) {
note.failHold();
delete activeHoldNotes[note.zone];
missCount++;
}
}
}
if (note.active && !note.isHit && !note.isMissed && !note.isSpawning) {
var timeDiff = currentTime - note.hitTime;
var scannerPassed = false;
if (scannerIsMovingUp && scanner.y < note.y - 50) {
scannerPassed = true;
} else if (!scannerIsMovingUp && scanner.y > note.y + 50) {
scannerPassed = true;
}
if (scannerPassed && timeDiff > 300) {
note.isMissed = true;
note.showMissEffect();
missCount++;
}
}
if (!note.active) {
notes.splice(i, 1);
if (game.children.indexOf(note) > -1) {
game.removeChild(note);
}
if (note.destroy) {
note.destroy();
}
}
}
if (currentSongData && currentTime > currentSongData.totalLength + 500 && currentGameState === GAME_STATE_PLAYING) {
LK.stopMusic();
gameStarted = false;
if (scanner) {
tween.stop(scanner);
}
var finalScore = LK.getScore();
// --- Letter Grade Calculation (MOVED HERE) ---
var totalNotes = 0;
if (currentSongData && currentSongData.notes) {
totalNotes = currentSongData.notes.length;
}
var grade = "C";
if (totalNotes > 0) {
if (missCount === 0 && perfectCount === totalNotes) {
grade = "S";
} else {
var hitCount = perfectCount + goodCount;
var ratio = hitCount / totalNotes;
if (ratio >= 0.95) {
grade = "A";
} else if (ratio >= 0.80) {
grade = "B";
} else {
grade = "C";
}
}
} else {
grade = "-";
}
var songName = null;
for (var key in SongDatabase) {
if (SongDatabase[key] === currentSongData) {
songName = key;
break;
}
}
if (songName) {
var wasUpdated = updateHighScore(songName, finalScore, grade); // Now grade is available
if (wasUpdated) {
// Update songSelectScreen items if they are already created
if (songSelectScreen.items) {
for (var j = 0; j < songSelectScreen.items.length; j++) {
var item = songSelectScreen.items[j];
if (item.songName === songName) {
// Assuming scoreText is the 4th child (index 3)
var scoreTextDisplay = item.children[3];
if (scoreTextDisplay && scoreTextDisplay.setText) {
var gradeDisplay = SongDatabase[songName].highestGrade || 'C';
scoreTextDisplay.setText('High Score: ' + SongDatabase[songName].highScore + ' | Grade: ' + gradeDisplay);
}
break;
}
}
}
}
if (resultsScreen.highScoreText) {
resultsScreen.highScoreText.setText("High Score: " + SongDatabase[songName].highScore);
}
}
if (resultsScreen.scoreText) {
resultsScreen.scoreText.setText("Final Score: " + finalScore);
}
if (resultsScreen.perfectText) {
resultsScreen.perfectText.setText("Perfect: " + perfectCount);
}
if (resultsScreen.goodText) {
resultsScreen.goodText.setText("Good: " + goodCount);
}
if (resultsScreen.missText) {
resultsScreen.missText.setText("Miss: " + missCount);
}
if (!resultsScreen.gradeText) {
resultsScreen.gradeText = new Text2('Grade: ' + grade, {
size: 120,
fill: 0x00FFFF
});
resultsScreen.gradeText.anchor.set(0.5, 0.5);
resultsScreen.gradeText.x = GAME_WIDTH / 2;
resultsScreen.gradeText.y = GAME_HEIGHT / 2 + 280;
resultsScreen.container.addChild(resultsScreen.gradeText);
} else {
resultsScreen.gradeText.setText('Grade: ' + grade);
resultsScreen.gradeText.visible = true;
}
showResultsScreen();
}
if (scanner && game.children.indexOf(scanner) > -1) {
game.removeChild(scanner);
game.addChild(scanner);
}
}; ===================================================================
--- original.js
+++ change.js
@@ -283,12 +283,10 @@
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
- duration: 700,
- // Adjusted duration for a more pronounced elastic effect
- easing: tween.elasticOut,
- // Use elastic easing
+ duration: 500,
+ easing: tween.easeOut,
onFinish: function onFinish() {
self.isSpawning = false;
self._initiateBeatPulseLoop();
}
@@ -330,55 +328,24 @@
};
self.showMissEffect = function () {
self._clearBeatPulseLoop();
LK.getSound('missSound').play();
- // Stop any ongoing spawn or other animations on the note itself
- tween.stop(self);
- // Tint the note graphics red quickly
- if (self.noteGraphics) {
- tween(self.noteGraphics, {
- tint: 0xff0000
- }, {
- duration: 100,
- // Quick tint
- onFinish: function onFinish() {
- // After tinting, start the reverse spawn animation on the note container
- tween(self, {
- alpha: 0,
- scaleX: 0.1,
- // Initial spawn scale
- scaleY: 0.1 // Initial spawn scale
- }, {
- duration: 500,
- // Duration for the reverse animation
- easing: tween.elasticIn,
- // Reverse of elasticOut or similar
- onFinish: function onFinish() {
- self.active = false;
- self._clearBeatPulseLoop();
- // Optionally, reset tint if note might be reused, though destroy is typical
- // if (self.noteGraphics) {
- // self.noteGraphics.tint = self.color;
- // }
- }
- });
- }
- });
- } else {
- // Fallback if noteGraphics is somehow not available
- tween(self, {
- alpha: 0,
- scaleX: 0.1,
- scaleY: 0.1
- }, {
- duration: 500,
- easing: tween.elasticIn,
- onFinish: function onFinish() {
- self.active = false;
- self._clearBeatPulseLoop();
- }
- });
- }
+ tween(self.noteGraphics, {
+ tint: 0xff0000
+ }, {
+ duration: 150,
+ onFinish: function onFinish() {
+ tween(self, {
+ alpha: 0.3
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ self.active = false;
+ self._clearBeatPulseLoop();
+ }
+ });
+ }
+ });
};
self.explodeIntoParticles = function () {
var noteScale = 1.2; // Base scale relative to noteCore (300px)
// Stage 1: Initial Flash (0-200ms)
@@ -1047,9 +1014,9 @@
var isDragging = false;
var dragStartX, dragStartY;
var currentTrail = [];
var backgroundParticles = [];
-var NUM_BACKGROUND_PARTICLES = 100;
+var NUM_BACKGROUND_PARTICLES = 150;
var titleScreen = {};
var songSelectScreen = {};
var songSelectScrollY = 0;
var maxScrollY = 0;
@@ -1095,12 +1062,16 @@
};
}
// Function to initiate a direction change
function changeParticleDirection() {
- if (isChangingDirection) return; // Already changing
+ if (isChangingDirection) {
+ return;
+ } // Already changing
// Generate a new direction 45-90 degrees from current
var angleChange = Math.PI / 4 + Math.random() * (Math.PI / 4); // 45-90 degrees
- if (Math.random() < 0.5) angleChange = -angleChange; // Random direction
+ if (Math.random() < 0.5) {
+ angleChange = -angleChange;
+ } // Random direction
var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x);
var newAngle = currentAngle + angleChange;
targetParticleDirection.x = Math.cos(newAngle);
targetParticleDirection.y = Math.sin(newAngle);
The word 'Pulsar' in a glowing neon SVG in futuristic font. The word is half blue on the left and half red on the right. In-Game asset. 2d. High contrast. No shadows
Remove the background.
A thin expanding ring with energy distortion ``` - Outer ring: 4-6 pixels thick, bright cyan (#00FFFF) - Inner ring: 2-3 pixels thick, white (#FFFFFF) - Ring thickness: Tapers from thick to thin as it expands - Transparency: Ring itself at 80% opacity - Background: Completely transparent - Edge treatment: Soft anti-aliased edges, slight glow effect - Optional: Subtle "energy crackle" texture within the ring. In-Game asset. 2d. High contrast. No shadows
Soft, lingering light effect ``` - Center: Warm orange (#FF6600) at 40% opacity - Middle: Yellow (#FFAA00) at 25% opacity - Edge: Transparent - Shape: Perfect circle with very soft, wide falloff. In-Game asset. 2d. High contrast. No shadows