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; // 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;
});
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 = 100;
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;
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();
if (backgroundParticles.length === 0) {
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);
}
} else {
backgroundParticles.forEach(function (p) {
p.init();
p.y = Math.random() * GAME_HEIGHT;
});
}
}
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
@@ -10,15 +10,12 @@
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
});
@@ -72,70 +69,59 @@
anchorY: 0.5
});
self.particleGraphics.tint = color;
self.baseSpeed = (0.5 + Math.random() * 1.5) * scale;
- self.speed = self.baseSpeed; // Initialize speed with 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();
+ // 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 * 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
+ 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;
- // Apply sine offset
- var sineX = self.initialX + perpX * sineOffset;
- var sineY = self.y + perpY * sineOffset; // Note: self.y is already updated, this adds sine relative to current y
- // Only apply sine to X if moving primarily vertically, and vice versa
- if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) {
- self.x = sineX; // Apply sine to X
- } else {
- // Apply sine to Y (Adjusting initialY for horizontal movement)
- // This logic might need refinement if initialY is meant to be a fixed vertical baseline
- // For now, let's keep it simple and apply sine to the current y
- self.y = self.y + perpY * sineOffset; // Apply sine to Y
- }
- // Reset particle if it goes off screen
- var margin = self.particleGraphics.height * self.initialScale;
+ 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) {
- self.init();
- // Start from appropriate edge based on current direction
- if (currentDirection.y > 0.5) {
- // Moving mostly down
- self.y = -Math.random() * 100 - margin;
- self.x = Math.random() * GAME_WIDTH;
- } else if (currentDirection.y < -0.5) {
- // Moving mostly up
- self.y = GAME_HEIGHT + Math.random() * 100 + margin;
- self.x = Math.random() * GAME_WIDTH;
- } else if (currentDirection.x > 0.5) {
- // Moving mostly right
- self.x = -Math.random() * 100 - margin;
- self.y = Math.random() * GAME_HEIGHT;
+ // 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 mostly left (or any other case)
- self.x = GAME_WIDTH + Math.random() * 100 + margin;
- self.y = Math.random() * GAME_HEIGHT;
+ // Moving up, spawn from bottom
+ newX = Math.random() * GAME_WIDTH;
+ newY = GAME_HEIGHT + margin - Math.random() * 50;
}
- self.initialX = self.x; // Reset initialX for sine wave calculation
- self.distanceTraveled = 0; // Reset distance for sine wave
+ self.x = newX;
+ self.y = newY;
}
};
self.init();
return self;
@@ -970,9 +956,9 @@
/****
* Game Code
****/
-// Add these global variables near the top with other game variables
+// Add these global variables (replace the previous ones)
var globalParticleDirection = {
x: 0,
y: 1
}; // Start moving downward
@@ -984,9 +970,10 @@
var directionChangeDuration = 1000; // 1 second
var isChangingDirection = false;
var lastDirectionChangeTime = 0;
var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds
-var countdownSpeedMultiplier = 1;
+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;
@@ -7984,8 +7971,9 @@
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 () {
@@ -7998,10 +7986,12 @@
LK.clearInterval(countdownInterval);
if (countdownTxt && countdownTxt.destroy) {
countdownTxt.destroy();
}
+ // Keep the accelerated speed when game starts
+ baseParticleSpeed = 5; // Maintain 5x speed
isCountdownActive = false;
- countdownSpeedMultiplier = 1; // Reset to normal speed
+ countdownAcceleration = 1;
startGame();
}
}, 1000);
// Smooth acceleration during countdown
@@ -8013,9 +8003,9 @@
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
+ countdownAcceleration = 1 + easedProgress * 4; // 1x to 5x speed
}, 50); // Update every 50ms for smooth acceleration
}
function showResultsScreen() {
currentGameState = GAME_STATE_RESULTS;
@@ -8107,8 +8097,12 @@
gameStarted = false;
perfectCount = 0;
goodCount = 0;
missCount = 0;
+ // Reset particle speed for new game
+ baseParticleSpeed = 1;
+ isCountdownActive = false;
+ countdownAcceleration = 1;
if (scoreTxt && scoreTxt.destroy) {
scoreTxt.destroy();
}
scoreTxt = new Text2('0', {
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