User prompt
Prevent note generation when key animation is not playing.
User prompt
Prevent note spawning in the top 4 square when animation is not playing.
User prompt
Notes spawn only when the animation plays in the top 4 square. Instantly spawn notes when the animation plays.
User prompt
Make all difficulty levels a little easier
User prompt
Spawn notes when animation plays in top 4 frames. Limit the maximum number of notes that can be on screen at the same time.
User prompt
Remove all note effects.
User prompt
Move the top 4 squares down a little. When an animation is played in these frames, a note will appear. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move the bottom 4 squares very high.
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. Remove all note animations. Then recreate button and note animations accordingly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function' in or related to this line: 'setTimeout(function () {' Line Number: 1453
User prompt
Please fix the bug: 'ReferenceError: notes is not defined' in or related to this line: 'for (var t = 0; t < notes.length; t++) {' Line Number: 1662
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. Remove all note animations. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Remix started
Copy Rhythm Rush
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundParticle = Container.expand(function () {
var self = Container.call(this);
// Create particle using small note asset
self.particleGraphics = self.attachAsset('chordNote2', {
anchorX: 0.5,
anchorY: 0.5
});
// Random properties
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.floatAmplitude = 20 + Math.random() * 30;
self.floatSpeed = 0.02 + Math.random() * 0.03;
self.initialY = self.y;
// Set random position
self.x = Math.random() * 2048;
self.y = Math.random() * 2732;
self.initialY = self.y;
// Set random size and transparency
var scale = 0.1 + Math.random() * 0.3;
self.particleGraphics.scaleX = scale;
self.particleGraphics.scaleY = scale;
self.particleGraphics.alpha = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff, 0xffffff];
self.particleGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Slow upward movement
self.y -= self.speed;
// Floating motion
self.x += Math.sin(LK.ticks * self.floatSpeed) * 0.5;
// Gentle rotation
self.particleGraphics.rotation += self.rotationSpeed;
// Music-reactive pulsing during gameplay
var basePulse = 1 + Math.sin(LK.ticks * 0.05) * 0.1;
var musicPulse = 1;
if (gameState === 'playing' && beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.3;
beatIntensity *= 0.95; // Decay beat intensity
}
var finalPulse = basePulse * musicPulse;
self.particleGraphics.scaleX = scale * finalPulse;
self.particleGraphics.scaleY = scale * finalPulse;
// Music-reactive color shifting
if (gameState === 'playing') {
var beatColor = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var musicColorShift = Math.floor(beatColor * 100);
self.particleGraphics.tint = self.particleGraphics.tint & 0xFF00FF | musicColorShift << 8;
}
// Reset particle when it goes off screen
if (self.y < -100) {
self.y = 2732 + 100;
self.x = Math.random() * 2048;
}
};
return self;
});
var BackgroundWave = Container.expand(function () {
var self = Container.call(this);
// Create wave using lane asset
self.waveGraphics = self.attachAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
// Wave properties
self.amplitude = 50 + Math.random() * 100;
self.frequency = 0.01 + Math.random() * 0.02;
self.speed = 0.5 + Math.random() * 1.5;
self.initialX = Math.random() * 2048;
self.direction = Math.random() > 0.5 ? 1 : -1;
// Set position and appearance
self.x = self.initialX;
self.y = Math.random() * 2732;
self.waveGraphics.alpha = 0.05 + Math.random() * 0.1;
self.waveGraphics.scaleX = 0.5 + Math.random() * 1.5;
self.waveGraphics.scaleY = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff];
self.waveGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Wave movement
self.x += self.speed * self.direction;
// Sine wave motion
self.y += Math.sin(LK.ticks * self.frequency) * 0.5;
// Gentle rotation
self.waveGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.2;
// Reset wave when it goes off screen
if (self.x < -200 || self.x > 2248) {
self.x = self.direction > 0 ? -200 : 2248;
self.y = Math.random() * 2732;
}
};
return self;
});
var Button = Container.expand(function (lane) {
var self = Container.call(this);
self.lane = lane;
self.isPressed = false;
self.buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
// Position at bottom of lane
self.x = lane * (2048 / 3) + 2048 / 6;
self.y = 2732 - 200;
self.down = function (x, y, obj) {
self.press();
};
self.press = function () {
if (self.isPressed) return;
self.isPressed = true;
// Create ripple wave effect
var ripple = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
ripple.x = self.x;
ripple.y = self.y;
ripple.alpha = 0.8;
ripple.scaleX = 0.8;
ripple.scaleY = 0.8;
ripple.tint = 0x00FFFF;
// Animate ripple expanding outward
tween(ripple, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (ripple && ripple.parent) {
ripple.destroy();
}
}
});
// Enhanced press animation with bounce
tween(self.buttonGraphics, {
alpha: 0.6,
scaleX: 0.85,
scaleY: 0.85,
rotation: Math.PI * 0.1
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
// Quick bounce back
tween(self.buttonGraphics, {
scaleX: 1.1,
scaleY: 1.1,
rotation: -Math.PI * 0.05
}, {
duration: 60,
easing: tween.bounceOut
});
}
});
// Check for beat timing accuracy when button is pressed
var currentTime = LK.ticks;
var timeSinceLastBeat = currentTime - lastBeatTime;
var beatWindow = beatInterval * 0.3; // 30% of beat interval for timing window
if (gameState === 'playing') {
var points = 0;
var feedbackText = '';
// Check if button press is close to a beat
if (timeSinceLastBeat <= beatWindow) {
// Perfect timing - very close to beat
points = 100;
feedbackText = 'PERFECT BEAT';
LK.getSound('hit').play();
} else if (timeSinceLastBeat <= beatWindow * 2) {
// Good timing - somewhat close to beat
points = 50;
feedbackText = 'GOOD BEAT';
LK.getSound('hit').play();
} else {
// Off beat
feedbackText = 'OFF BEAT';
LK.getSound('miss').play();
}
if (points > 0) {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
updateCombo(true);
} else {
updateCombo(false);
}
// Show feedback text
feedbackTxt.setText(feedbackText);
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
// Create score animation if points were earned
if (points > 0) {
var scoreText = new Text2('+' + points, {
size: 50,
fill: points === 100 ? 0xFFD700 : 0x00FFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = self.x;
scoreText.y = self.y - 100;
scoreText.alpha = 1;
game.addChild(scoreText);
tween(scoreText, {
y: scoreText.y - 150,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (scoreText && scoreText.parent) {
scoreText.destroy();
}
}
});
}
}
// Reset button after short delay with smooth animation
LK.setTimeout(function () {
self.isPressed = false;
// Smooth release animation
tween(self.buttonGraphics, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.elasticOut
});
}, 100);
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
self.keyGraphics = self.attachAsset('key', {
anchorX: 0.5,
anchorY: 0.5
});
// Position key in center of screen
self.x = 2048 / 2;
self.y = 2732 / 2;
// Key animation properties
self.baseScale = 1.0;
self.isAnimating = false;
// Beat animation method
self.animateOnBeat = function (intensity) {
if (self.isAnimating) return;
self.isAnimating = true;
// Scale and color effect based on beat intensity
var targetScale = self.baseScale + intensity * 0.5;
var beatColor = intensity > 0.7 ? 0xFFD700 : intensity > 0.4 ? 0x00FFFF : 0xFFFFFF;
// Store original values
var originalScale = self.keyGraphics.scaleX;
var originalTint = self.keyGraphics.tint;
// Quick press animation
tween(self.keyGraphics, {
scaleX: targetScale * 0.8,
scaleY: targetScale * 0.8,
tint: beatColor
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
// Release animation
tween(self.keyGraphics, {
scaleX: targetScale,
scaleY: targetScale
}, {
duration: 100,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Return to normal
tween(self.keyGraphics, {
scaleX: originalScale,
scaleY: originalScale,
tint: originalTint
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
}
});
// Add glow effect for strong beats
if (intensity > 0.7) {
var glowRing = game.addChild(LK.getAsset('key', {
anchorX: 0.5,
anchorY: 0.5
}));
glowRing.x = self.x;
glowRing.y = self.y;
glowRing.keyGraphics = glowRing.children[0];
glowRing.keyGraphics.alpha = 0.6;
glowRing.keyGraphics.tint = 0xFFD700;
glowRing.keyGraphics.scaleX = 0.8;
glowRing.keyGraphics.scaleY = 0.8;
tween(glowRing.keyGraphics, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (glowRing && glowRing.parent) {
glowRing.destroy();
}
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing', or 'gameOver'
// Background effects
var backgroundParticles = [];
var backgroundWaves = [];
var startButton;
var gameOverButton;
var difficultyButtons = [];
var currentDifficulty = -1; // -1=None selected, 0=Easy, 1=Medium, 2=Hard, 3=Expert
var difficultySelected = false;
var musicButtons = [];
var currentMusic = -1; // -1=None selected, 0=Music1, 1=Music2, 2=Music3
var musicTracks = [{
name: '1',
id: 'GameMusic1',
color: 0x8800ff
}, {
name: '2',
id: 'GameMusic2',
color: 0xff8800
}, {
name: '3',
id: 'GameMusic3',
color: 0x0088ff
}, {
name: '4',
id: 'GameMusic4',
color: 0x00ff88
}, {
name: '5',
id: 'GameMusic5',
color: 0xff0088
}, {
name: '6',
id: 'GameMusic6',
color: 0x88ff00
}];
var difficulties = [{
name: 'EASY',
spawnInterval: 120,
maxNotes: 4,
timeLimit: 60,
noteSpeedMultiplier: 0.8,
color: 0x00FF00
}, {
name: 'MEDIUM',
spawnInterval: 80,
maxNotes: 6,
timeLimit: 60,
noteSpeedMultiplier: 1.2,
color: 0xFFFF00
}, {
name: 'HARD',
spawnInterval: 50,
maxNotes: 8,
timeLimit: 60,
noteSpeedMultiplier: 1.6,
color: 0xFF8800
}, {
name: 'EXPERT',
spawnInterval: 30,
maxNotes: 12,
timeLimit: 60,
noteSpeedMultiplier: 2.0,
color: 0xFF0000
}];
// Game variables
var buttons = [];
var timingLineY = 2732 - 500;
var bottomLineY = 2732 - 2; // Position miss line at bottom edge of screen
// Key and beat synchronization variables
var visualKey = null;
var musicBeat = 0;
var beatInterval = 30; // frames per beat (adjustable based on music tempo)
var beatIntensity = 0;
var lastBeatTime = 0;
var bpmSettings = {
'GameMusic1': 120,
// 120 BPM
'GameMusic2': 128,
// 128 BPM
'GameMusic3': 110,
// 110 BPM
'GameMusic4': 140,
// 140 BPM
'GameMusic5': 100,
// 100 BPM
'GameMusic6': 150 // 150 BPM
};
var currentBPM = 120;
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane1.x = 2048 / 3;
lane1.y = 0;
lane1.visible = false; // Hide initially
var lane2 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane2.x = 2048 / 3 * 2;
lane2.y = 0;
lane2.visible = false; // Hide initially
// Create timing line
var timingLine = game.addChild(LK.getAsset('timingLine', {
anchorX: 0,
anchorY: 0.5
}));
timingLine.x = 0;
timingLine.y = timingLineY;
timingLine.visible = false; // Hide initially
// Create bottom line
var bottomLine = game.addChild(LK.getAsset('bottomLine', {
anchorX: 0,
anchorY: 0.5
}));
bottomLine.x = 0;
bottomLine.y = bottomLineY;
bottomLine.visible = false; // Hide initially
// Create start menu elements
var titleTxt = new Text2('RHYTHM GAME', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 2732 / 2 - 200;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.5;
titleTxt.scaleY = 0.5;
game.addChild(titleTxt);
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Add continuous floating animation
function animateTitleFloat() {
tween(titleTxt, {
y: titleTxt.y - 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleTxt, {
y: titleTxt.y + 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateTitleFloat
});
}
});
}
LK.setTimeout(animateTitleFloat, 1500);
// Add breathing effect to background elements
function createBreathingEffect() {
// Animate lane separators with subtle breathing
tween(lane1, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane1, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: createBreathingEffect
});
}
});
// Offset timing for lane2
LK.setTimeout(function () {
tween(lane2, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane2, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut
});
}
});
}, 1500);
}
// Start breathing effect when game elements are visible
var breathingEffectStarted = false;
var startButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
startButtonShape.x = 2048 / 2;
startButtonShape.y = 2732 / 2 + 100;
startButtonShape.tint = 0x00AA00;
startButtonShape.alpha = 0;
startButtonShape.scaleX = 0;
startButtonShape.scaleY = 0;
// Animate start button appearing
tween(startButtonShape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1200
});
// Add pulsing glow effect to start button
function animateStartButtonPulse() {
tween(startButtonShape, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x00FF00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonShape, {
scaleX: 1,
scaleY: 1,
tint: 0x00AA00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateStartButtonPulse
});
}
});
}
LK.setTimeout(animateStartButtonPulse, 2000);
var startButtonTxt = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonTxt.anchor.set(0.5, 0.5);
startButtonTxt.x = 2048 / 2 + 300;
startButtonTxt.y = 2732 / 2 + 100;
startButtonTxt.alpha = 0;
game.addChild(startButtonTxt);
// Animate start button text sliding in
tween(startButtonTxt, {
x: 2048 / 2,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
delay: 1400
});
// Create difficulty selection text
var difficultyTitleTxt = new Text2('SELECT DIFFICULTY', {
size: 80,
fill: 0xFFFFFF
});
difficultyTitleTxt.anchor.set(0.5, 0.5);
difficultyTitleTxt.x = 2048 / 2;
difficultyTitleTxt.y = 2732 / 2 + 300;
game.addChild(difficultyTitleTxt);
// Create difficulty selection indicator text
var difficultyIndicatorTxt = new Text2('None selected', {
size: 60,
fill: 0xFF0000
});
difficultyIndicatorTxt.anchor.set(0.5, 0.5);
difficultyIndicatorTxt.x = 2048 / 2;
difficultyIndicatorTxt.y = 2732 / 2 + 500;
difficultyIndicatorTxt.alpha = 0;
game.addChild(difficultyIndicatorTxt);
// Create music selection text
var musicTitleTxt = new Text2('SELECT MUSIC', {
size: 70,
fill: 0xFFFFFF
});
musicTitleTxt.anchor.set(0.5, 0.5);
musicTitleTxt.x = 2048 / 2;
musicTitleTxt.y = 2732 / 2 + 650;
game.addChild(musicTitleTxt);
// Create music selection indicator text
var musicIndicatorTxt = new Text2('None selected', {
size: 50,
fill: 0xFF0000
});
musicIndicatorTxt.anchor.set(0.5, 0.5);
musicIndicatorTxt.x = 2048 / 2;
musicIndicatorTxt.y = 2732 / 2 + 1130;
musicIndicatorTxt.alpha = 0;
game.addChild(musicIndicatorTxt);
// Create difficulty buttons
for (var d = 0; d < difficulties.length; d++) {
var diffButton = {
index: d,
text: new Text2(difficulties[d].name, {
size: 50,
fill: difficulties[d].color
})
};
diffButton.text.anchor.set(0.5, 0.5);
diffButton.text.x = d * 400 + 424; // Spread across screen
diffButton.text.y = 2732 / 2 + 400; // Below the "select difficulty" text
diffButton.text.scaleX = 0;
diffButton.text.scaleY = 0;
diffButton.text.alpha = 0;
game.addChild(diffButton.text);
difficultyButtons.push(diffButton);
// Animate text appearing with staggered timing
tween(diffButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: d * 150
});
}
// Animate difficulty title appearing
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator appearing
tween(difficultyIndicatorTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 800
});
// Create music selection buttons
for (var m = 0; m < musicTracks.length; m++) {
var musicButton = {
index: m,
shape: game.addChild(LK.getAsset('musicButton', {
anchorX: 0.5,
anchorY: 0.5
})),
text: new Text2(musicTracks[m].name, {
size: 40,
fill: 0xFFFFFF
})
};
// Position buttons in 2 rows of 3
var row = Math.floor(m / 3);
var col = m % 3;
musicButton.shape.x = col * 300 + 724; // Spread across screen
musicButton.shape.y = 2732 / 2 + 800 + row * 200;
musicButton.shape.tint = musicTracks[m].color;
musicButton.shape.scaleX = 0;
musicButton.shape.scaleY = 0;
musicButton.shape.alpha = 0;
musicButton.text.anchor.set(0.5, 0.5);
musicButton.text.x = musicButton.shape.x;
musicButton.text.y = musicButton.shape.y;
musicButton.text.scaleX = 0;
musicButton.text.scaleY = 0;
musicButton.text.alpha = 0;
game.addChild(musicButton.text);
musicButtons.push(musicButton);
// Animate buttons appearing with staggered timing
tween(musicButton.shape, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
tween(musicButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1200 + m * 100
});
}
// Animate music title appearing
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator appearing
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1000
});
// No initial music selection - all buttons start at normal size
// Initialize background effects
function createBackgroundEffects() {
// Create floating particles
for (var p = 0; p < 15; p++) {
var particle = new BackgroundParticle();
backgroundParticles.push(particle);
game.addChildAt(particle, 0); // Add behind other elements
}
// Create background waves
for (var w = 0; w < 5; w++) {
var wave = new BackgroundWave();
backgroundWaves.push(wave);
game.addChildAt(wave, 0); // Add behind other elements
}
}
// Start background effects
createBackgroundEffects();
// Add dynamic background color transitions
var backgroundColors = [0x001122, 0x220011, 0x112200, 0x001100];
var currentBgColorIndex = 0;
var bgColorTransitionTimer = 0;
function updateBackgroundColor() {
bgColorTransitionTimer++;
// Music-reactive beat pulsing
if (gameState === 'playing') {
musicBeat++;
if (musicBeat >= beatInterval) {
musicBeat = 0;
beatIntensity = 1.0;
// Flash background on beat
var currentBgColor = backgroundColors[currentBgColorIndex];
var brightColor = {
r: Math.min(255, (currentBgColor >> 16 & 0xFF) + 30),
g: Math.min(255, (currentBgColor >> 8 & 0xFF) + 30),
b: Math.min(255, (currentBgColor & 0xFF) + 30)
};
var flashColor = brightColor.r << 16 | brightColor.g << 8 | brightColor.b;
game.setBackgroundColor(flashColor);
// Fade back to normal
tween({
intensity: 1
}, {
intensity: 0
}, {
duration: beatInterval * 16,
easing: tween.easeOut,
onUpdate: function onUpdate() {
var fade = this.intensity;
var newR = Math.floor((currentBgColor >> 16 & 0xFF) + (brightColor.r - (currentBgColor >> 16 & 0xFF)) * fade);
var newG = Math.floor((currentBgColor >> 8 & 0xFF) + (brightColor.g - (currentBgColor >> 8 & 0xFF)) * fade);
var newB = Math.floor((currentBgColor & 0xFF) + (brightColor.b - (currentBgColor & 0xFF)) * fade);
game.setBackgroundColor(newR << 16 | newG << 8 | newB);
}
});
}
}
if (bgColorTransitionTimer >= 600) {
// Change every 10 seconds
bgColorTransitionTimer = 0;
var nextColorIndex = (currentBgColorIndex + 1) % backgroundColors.length;
// Smooth color transition using tween
var currentColor = backgroundColors[currentBgColorIndex];
var nextColor = backgroundColors[nextColorIndex];
// Create temporary object to animate color values
var colorTransition = {
r: currentColor >> 16 & 0xFF,
g: currentColor >> 8 & 0xFF,
b: currentColor & 0xFF
};
var targetR = nextColor >> 16 & 0xFF;
var targetG = nextColor >> 8 & 0xFF;
var targetB = nextColor & 0xFF;
tween(colorTransition, {
r: targetR,
g: targetG,
b: targetB
}, {
duration: 3000,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
var newColor = Math.floor(colorTransition.r) << 16 | Math.floor(colorTransition.g) << 8 | Math.floor(colorTransition.b);
game.setBackgroundColor(newColor);
},
onFinish: function onFinish() {
currentBgColorIndex = nextColorIndex;
}
});
}
}
// No initial difficulty highlighting since none is selected
// Create start button handler
startButton = {
shape: startButtonShape,
text: startButtonTxt,
down: function down(x, y, obj) {
this.startGame();
},
startGame: function startGame() {
// Check if difficulty is selected
if (currentDifficulty === -1) {
// Flash difficulty title to indicate selection is required
tween(difficultyTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Check if music is selected
if (currentMusic === -1) {
// Flash music title to indicate selection is required
tween(musicTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Stop all music for 3 seconds and play start sound
LK.stopMusic();
LK.getSound('Gameisstart').play();
// Create enhanced zoom animation sequence with pulsing and glow effects
// Initial button press feedback - quick scale down
tween(this.shape, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 1: Zoom out with rotation and color change
tween(startButton.shape, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7,
rotation: Math.PI * 2,
tint: 0x00FFFF
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 2: Explosive zoom in with multiple pulses
tween(startButton.shape, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0,
rotation: Math.PI * 4,
tint: 0xFFFFFF
}, {
duration: 2000,
easing: tween.elasticOut
});
// Create multiple expanding rings for more dramatic effect
for (var r = 0; r < 3; r++) {
var expandingRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
expandingRing.x = startButton.shape.x;
expandingRing.y = startButton.shape.y;
expandingRing.tint = r === 0 ? 0x00FF00 : r === 1 ? 0x00FFFF : 0xFFFFFF;
expandingRing.alpha = 0.6;
expandingRing.scaleX = 0.5;
expandingRing.scaleY = 0.5;
// Animate each ring with staggered timing
tween(expandingRing, {
scaleX: 6.0 + r * 2,
scaleY: 6.0 + r * 2,
alpha: 0,
rotation: Math.PI * (2 + r)
}, {
duration: 2500 + r * 200,
easing: tween.easeOut,
delay: r * 150,
onFinish: function onFinish() {
if (expandingRing && expandingRing.parent) {
expandingRing.destroy();
}
}
});
}
}
});
}
});
// Text animation with bounce and glow effect
tween(this.text, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Text zoom out with glow
tween(startButton.text, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8,
tint: 0xFFD700
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Text explosive zoom with sparkle effect
tween(startButton.text, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0,
tint: 0xFFFFFF
}, {
duration: 2000,
easing: tween.bounceOut
});
}
});
}
});
// Hide menu elements immediately
titleTxt.visible = false;
// Hide difficulty selection
difficultyTitleTxt.visible = false;
difficultyIndicatorTxt.visible = false;
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = false;
}
// Hide music selection
musicTitleTxt.visible = false;
musicIndicatorTxt.visible = false;
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = false;
musicButtons[m].text.visible = false;
}
// Start the actual game after 3 seconds
LK.setTimeout(function () {
// Hide start button elements after animation
startButton.shape.visible = false;
startButton.text.visible = false;
// Show game elements
timerTxt.visible = true;
scoreTxt.visible = true;
timingLine.visible = true;
bottomLine.visible = true;
lane1.visible = true;
lane2.visible = true;
// Create game buttons
for (var i = 0; i < 3; i++) {
var button = new Button(i);
buttons.push(button);
game.addChild(button);
}
// Create visual key for beat synchronization
visualKey = new Key();
game.addChild(visualKey);
// Play selected music and set BPM
var selectedMusicId = musicTracks[currentMusic].id;
LK.playMusic(selectedMusicId);
currentBPM = bpmSettings[selectedMusicId] || 120;
beatInterval = Math.floor(3600 / currentBPM); // Convert BPM to frames (60fps)
// Apply difficulty settings
var difficulty = difficulties[currentDifficulty];
timeRemaining = difficulty.timeLimit;
// Start the game
gameState = 'playing';
LK.setScore(0);
scoreTxt.setText('0');
timerTxt.setText(timeRemaining.toString());
}, 3000);
}
};
// Initially hide game elements
var gameElements = [];
// Create countdown timer
var timeRemaining = 60;
// Create timer display above score
var timerTxt = new Text2('60', {
size: 80,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
timerTxt.x = 2048 / 2;
timerTxt.y = 2732 / 2 - 300;
timerTxt.visible = false; // Hide initially
game.addChild(timerTxt);
// Create score display in center of game area
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 2732 / 2 - 200;
scoreTxt.visible = false; // Hide initially
game.addChild(scoreTxt);
// Create feedback text display
var feedbackTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
feedbackTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(feedbackTxt);
feedbackTxt.alpha = 0;
// Create game over menu elements
var gameOverTxt = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 2048 / 2;
gameOverTxt.y = 2732 / 2 - 300;
gameOverTxt.visible = false;
game.addChild(gameOverTxt);
var finalScoreTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
finalScoreTxt.anchor.set(0.5, 0.5);
finalScoreTxt.x = 2048 / 2;
finalScoreTxt.y = 2732 / 2 - 150;
finalScoreTxt.visible = false;
game.addChild(finalScoreTxt);
var restartButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
restartButtonShape.x = 2048 / 2;
restartButtonShape.y = 2732 / 2 + 100;
restartButtonShape.tint = 0x0066CC;
restartButtonShape.visible = false;
var restartButtonTxt = new Text2('RESTART', {
size: 60,
fill: 0xFFFFFF
});
restartButtonTxt.anchor.set(0.5, 0.5);
restartButtonTxt.x = 2048 / 2;
restartButtonTxt.y = 2732 / 2 + 100;
restartButtonTxt.visible = false;
game.addChild(restartButtonTxt);
// Create restart button handler
gameOverButton = {
shape: restartButtonShape,
text: restartButtonTxt,
down: function down(x, y, obj) {
this.restartGame();
},
restartGame: function restartGame() {
// Stop all music when restart is pressed
LK.stopMusic();
// Hide game over elements
gameOverTxt.visible = false;
finalScoreTxt.visible = false;
this.shape.visible = false;
this.text.visible = false;
// Clear existing buttons and key
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
// Clean up visual key if it exists
if (visualKey && visualKey.parent) {
visualKey.destroy();
visualKey = null;
}
// Show menu elements with smooth animations
titleTxt.visible = true;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.8;
titleTxt.scaleY = 0.8;
startButton.shape.visible = true;
startButton.shape.alpha = 0;
startButton.shape.scaleX = 0;
startButton.shape.scaleY = 0;
startButton.text.visible = true;
startButton.text.alpha = 0;
startButton.text.x = startButton.text.x + 200;
difficultyTitleTxt.visible = true;
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
difficultyIndicatorTxt.visible = true;
difficultyIndicatorTxt.alpha = 0;
musicTitleTxt.visible = true;
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
musicIndicatorTxt.visible = true;
musicIndicatorTxt.alpha = 0;
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.elasticOut
});
// Animate start button
tween(startButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate start button text sliding in
tween(startButton.text, {
alpha: 1,
x: startButton.text.x - 200
}, {
duration: 500,
easing: tween.easeOut,
delay: 600
});
// Animate difficulty title
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator
tween(difficultyIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 800
});
// Animate difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = true;
difficultyButtons[d].text.alpha = 0;
difficultyButtons[d].text.scaleX = 0;
difficultyButtons[d].text.scaleY = 0;
// Staggered animation for each button
tween(difficultyButtons[d].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 600 + d * 100
});
}
// Animate music title
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
// Animate music buttons
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = true;
musicButtons[m].shape.alpha = 0;
musicButtons[m].shape.scaleX = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].shape.scaleY = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].text.visible = true;
musicButtons[m].text.alpha = 0;
musicButtons[m].text.scaleX = 0;
musicButtons[m].text.scaleY = 0;
// Staggered animation for each button
tween(musicButtons[m].shape, {
alpha: 1,
scaleX: currentMusic === m && currentMusic !== -1 ? 1.2 : 1,
scaleY: currentMusic === m && currentMusic !== -1 ? 1.2 : 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 800 + m * 100
});
tween(musicButtons[m].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
}
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Reset game state to menu
gameState = 'menu';
spawnTimer = 0;
LK.setScore(0);
}
};
// Create explosion animation function
function createExplosion(x, y, color) {
var particleCount = 8;
var particles = [];
for (var i = 0; i < particleCount; i++) {
// Create particle using small ellipse shape
var particle = game.addChild(LK.getAsset('fastNote', {
anchorX: 0.5,
anchorY: 0.5
}));
particle.x = x;
particle.y = y;
particle.tint = color || 0xFFFFFF;
particle.scaleX = 0.3;
particle.scaleY = 0.3;
particles.push(particle);
// Calculate random direction for each particle
var angle = i / particleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var distance = 100 + Math.random() * 50;
var targetX = x + Math.cos(angle) * distance;
var targetY = y + Math.sin(angle) * distance;
// Animate particle outward and fade out
tween(particle, {
x: targetX,
y: targetY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.parent) {
particle.destroy();
}
}
});
}
}
// Create screen shake effect
function shakeScreen(intensity, duration) {
var originalX = game.x;
var originalY = game.y;
var shakeStartTime = LK.ticks;
var shakeDuration = duration || 300;
var shakeIntensity = intensity || 20;
// Create shake animation
function shakeUpdate() {
var elapsed = LK.ticks - shakeStartTime;
if (elapsed < shakeDuration) {
var progress = elapsed / shakeDuration;
var currentIntensity = shakeIntensity * (1 - progress);
game.x = originalX + (Math.random() - 0.5) * currentIntensity * 2;
game.y = originalY + (Math.random() - 0.5) * currentIntensity * 2;
LK.setTimeout(shakeUpdate, 16); // ~60fps
} else {
game.x = originalX;
game.y = originalY;
}
}
shakeUpdate();
}
// Create combo streak effects
var comboCount = 0;
var lastHitTime = 0;
function updateCombo(isHit) {
if (isHit) {
var currentTime = LK.ticks;
if (currentTime - lastHitTime < 180) {
// Within 3 seconds
comboCount++;
} else {
comboCount = 1;
}
lastHitTime = currentTime;
// Show combo effects for streaks
if (comboCount >= 5 && comboCount % 5 === 0) {
// Create combo text
var comboText = new Text2('COMBO x' + comboCount, {
size: 60,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 2048 / 2;
comboText.y = 2732 / 2 + 100;
comboText.alpha = 0;
game.addChild(comboText);
// Animate combo text
tween(comboText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboText, {
alpha: 0,
y: comboText.y - 100
}, {
duration: 500,
onFinish: function onFinish() {
if (comboText && comboText.parent) {
comboText.destroy();
}
}
});
}
});
}
} else {
comboCount = 0;
}
}
// Beat detection and key animation trigger
function detectBeat() {
musicBeat++;
if (musicBeat >= beatInterval) {
musicBeat = 0;
lastBeatTime = LK.ticks;
// Calculate beat intensity based on game progression
beatIntensity = 0.8 + Math.random() * 0.4; // Random intensity between 0.8-1.2
// Trigger key animation if visual key exists
if (visualKey && gameState === 'playing') {
visualKey.animateOnBeat(beatIntensity);
}
// Create beat effects around the key
if (beatIntensity > 0.7) {
createExplosion(2048 / 2, 2732 / 2, 0x00FFFF);
}
// Beat intensity decay over time
setTimeout(function () {
beatIntensity *= 0.95;
}, 100);
}
}
// Handle clicks for start button
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if click is on start button
var dx = x - startButton.shape.x;
var dy = y - startButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
startButton.down(x, y, obj);
}
// Check if click is on difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
var diffButton = difficultyButtons[d];
var ddx = x - diffButton.text.x;
var ddy = y - diffButton.text.y;
var ddistance = Math.sqrt(ddx * ddx + ddy * ddy);
if (ddistance <= 100) {
// Text button hit area
// Reset previous selection if any
if (currentDifficulty !== -1) {
difficultyButtons[currentDifficulty].text.scaleX = 1.0;
difficultyButtons[currentDifficulty].text.scaleY = 1.0;
}
// Set new selection
currentDifficulty = d;
difficultySelected = true;
diffButton.text.scaleX = 1.0;
diffButton.text.scaleY = 1.0;
// Update indicator text to show selected difficulty
if (d === 0) {
// Easy mode - show in red
difficultyIndicatorTxt.setText('Easy mode selected');
difficultyIndicatorTxt.fill = 0xFF0000;
difficultyIndicatorTxt.tint = 0xFF0000;
} else {
difficultyIndicatorTxt.setText(difficulties[d].name.toLowerCase() + ' mode selected');
difficultyIndicatorTxt.fill = difficulties[d].color;
difficultyIndicatorTxt.tint = difficulties[d].color;
}
// Add selection effect
tween(diffButton.text, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(diffButton.text, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Animate the indicator text to show selection
tween(difficultyIndicatorTxt, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
break;
}
}
// Check if click is on music buttons
for (var m = 0; m < musicButtons.length; m++) {
var musicButton = musicButtons[m];
var mdx = x - musicButton.shape.x;
var mdy = y - musicButton.shape.y;
var mdistance = Math.sqrt(mdx * mdx + mdy * mdy);
if (mdistance <= 90) {
// Button radius
// Reset previous selection
if (currentMusic !== -1) {
musicButtons[currentMusic].shape.scaleX = 1.0;
musicButtons[currentMusic].shape.scaleY = 1.0;
}
// Set new selection
currentMusic = m;
musicButton.shape.scaleX = 1.0;
musicButton.shape.scaleY = 1.0;
// Update indicator text
if (m === 2) {
musicIndicatorTxt.setText('3 selected');
} else if (m === 3) {
musicIndicatorTxt.setText('4 selected');
} else {
musicIndicatorTxt.setText(musicTracks[m].name + ' selected');
}
musicIndicatorTxt.fill = musicTracks[m].color;
musicIndicatorTxt.tint = musicTracks[m].color;
// Add selection effect
tween(musicButton.shape, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut
});
// Animate the indicator text
tween(musicIndicatorTxt, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Play the selected music
LK.stopMusic();
LK.playMusic(musicTracks[m].id);
break;
}
}
} else if (gameState === 'gameOver') {
// Check if click is on restart button
var dx = x - gameOverButton.shape.x;
var dy = y - gameOverButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
gameOverButton.down(x, y, obj);
}
}
};
game.update = function () {
// Update background effects regardless of game state
updateBackgroundColor();
// Update background particles
for (var p = 0; p < backgroundParticles.length; p++) {
if (backgroundParticles[p] && backgroundParticles[p].update) {
backgroundParticles[p].update();
}
}
// Update background waves
for (var w = 0; w < backgroundWaves.length; w++) {
if (backgroundWaves[w] && backgroundWaves[w].update) {
backgroundWaves[w].update();
}
}
// Start breathing effect when appropriate
if (!breathingEffectStarted && gameState === 'playing') {
breathingEffectStarted = true;
createBreathingEffect();
}
// Add music-synchronized button effects during menu
if (gameState === 'menu' && currentMusic !== -1) {
// Create gentle music-reactive effects for menu buttons
var menuMusicBeat = Math.sin(LK.ticks * 0.1) * 0.5 + 0.5;
// Animate start button with music
if (startButton && startButton.shape && startButton.shape.visible) {
var startPulse = 1 + menuMusicBeat * 0.05;
startButton.shape.scaleX = startPulse;
startButton.shape.scaleY = startPulse;
// Subtle color shift
var startColorShift = Math.floor(menuMusicBeat * 50);
startButton.shape.tint = 0x00AA00 + (startColorShift << 8);
}
// Animate music selection buttons with staggered timing
for (var mb = 0; mb < musicButtons.length; mb++) {
if (musicButtons[mb] && musicButtons[mb].shape && musicButtons[mb].shape.visible) {
var staggeredBeat = Math.sin(LK.ticks * 0.08 + mb * 0.8) * 0.5 + 0.5;
var buttonPulse = 1 + staggeredBeat * 0.03;
// Scale animation
if (currentMusic !== mb) {
musicButtons[mb].shape.scaleX = buttonPulse;
musicButtons[mb].shape.scaleY = buttonPulse;
}
// Color breathing effect
var colorIntensity = staggeredBeat * 0.3 + 0.7;
var originalColor = musicTracks[mb].color;
var r = Math.floor((originalColor >> 16 & 0xFF) * colorIntensity);
var g = Math.floor((originalColor >> 8 & 0xFF) * colorIntensity);
var b = Math.floor((originalColor & 0xFF) * colorIntensity);
musicButtons[mb].shape.tint = r << 16 | g << 8 | b;
}
}
}
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
// Pulsing effect based on beat intensity
var pulseIntensity = 1 + beatIntensity * 0.3;
var basePulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1 * pulseIntensity;
// Add music beat synchronization
var musicPulse = 1;
if (beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.4;
// Create beat wave along timing line
timingLine.scaleX = 1 + beatIntensity * 0.1;
}
timingLine.scaleY = basePulse * musicPulse;
timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.2;
// Music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.05) * 0.5 + 0.5;
var baseColor = 0xFFFFFF;
// Beat-reactive color effects
if (beatIntensity > 0.3) {
var colorShift = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
baseColor = 0xFFFFFF + (Math.floor(colorShift * 50) << 16);
}
// Add music-reactive blue/cyan tint
var musicTint = Math.floor(musicColorCycle * 255);
timingLine.tint = baseColor & 0xFF0000 | musicTint << 8 | 0xFF;
}
// Add music-synchronized glow effect to buttons
for (var b = 0; b < buttons.length; b++) {
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var baseGlow = Math.sin(LK.ticks * 0.08 + b) * 0.1 + 0.9;
var musicGlow = 1;
// Music beat detection for synchronized effects
var isOnBeat = beatIntensity > 0.7;
var isMediumBeat = beatIntensity > 0.4;
if (beatIntensity > 0) {
musicGlow = 1 + beatIntensity * 0.3;
// Enhanced beat-reactive scaling with smooth transitions
var targetScaleX = 1 + beatIntensity * 0.15;
var targetScaleY = 1 + beatIntensity * 0.15;
// Smooth scaling animation to target with additional null check
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
tween(buttons[b].buttonGraphics, {
scaleX: targetScaleX,
scaleY: targetScaleY
}, {
duration: 100,
easing: tween.easeOut
});
}
// Strong beat effects
if (isOnBeat && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Pulse ring effect around button
var beatRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
beatRing.x = buttons[b].x;
beatRing.y = buttons[b].y;
beatRing.alpha = 0.6;
beatRing.scaleX = 0.8;
beatRing.scaleY = 0.8;
beatRing.tint = 0x00FFFF;
// Animate ring expanding and fading
tween(beatRing, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (beatRing && beatRing.parent) {
beatRing.destroy();
}
}
});
// Color flash effect on strong beats
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var originalTint = buttons[b].buttonGraphics.tint;
buttons[b].buttonGraphics.tint = 0xFFFFFF;
tween(buttons[b].buttonGraphics, {
tint: originalTint
}, {
duration: 200,
easing: tween.easeOut
});
}
}
// Medium beat effects
if (isMediumBeat && !isOnBeat && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Subtle glow pulse
var originalAlpha = buttons[b].buttonGraphics.alpha;
tween(buttons[b].buttonGraphics, {
alpha: Math.min(1.0, originalAlpha * 1.3)
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
tween(buttons[b].buttonGraphics, {
alpha: originalAlpha
}, {
duration: 150,
easing: tween.easeOut
});
}
}
});
}
}
// Continuous music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.1 + b * 1.2) * 0.5 + 0.5;
var baseColor = 0x00FF00; // Default green
// Apply music-reactive color shifts
if (beatIntensity > 0.2) {
var beatColorR = Math.floor((musicColorCycle + beatIntensity * 0.5) * 150) % 255;
var beatColorG = Math.floor(musicColorCycle * 255);
var beatColorB = Math.floor((1 - musicColorCycle + beatIntensity * 0.3) * 200) % 255;
baseColor = beatColorR << 16 | beatColorG << 8 | beatColorB;
}
// Apply subtle rotation on beats
if (beatIntensity > 0.5 && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var rotationAmount = beatIntensity * 0.1 * Math.sin(LK.ticks * 0.2 + b);
buttons[b].buttonGraphics.rotation = rotationAmount;
} else if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Return to neutral rotation smoothly
if (Math.abs(buttons[b].buttonGraphics.rotation) > 0.01) {
tween(buttons[b].buttonGraphics, {
rotation: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Apply final alpha and color
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
buttons[b].buttonGraphics.alpha = buttons[b].isPressed ? 0.7 : baseGlow * musicGlow;
if (!buttons[b].isPressed) {
buttons[b].buttonGraphics.tint = baseColor;
}
}
}
}
// Add music-synchronized flowing effects to lane separators
if (lane1.visible && lane2.visible) {
// Flowing light effect down the lanes with music sync
var baseFlow = Math.sin(LK.ticks * 0.1) * 0.3 + 0.7;
var musicFlow = 1;
if (beatIntensity > 0) {
musicFlow = 1 + beatIntensity * 0.5;
}
lane1.alpha = baseFlow * musicFlow;
lane2.alpha = baseFlow * musicFlow;
// Music-reactive width pulsing
var baseWidthPulse = 1 + Math.sin(LK.ticks * 0.05) * 0.2;
var musicWidthPulse = 1;
if (beatIntensity > 0) {
musicWidthPulse = 1 + beatIntensity * 0.3;
}
lane1.scaleX = baseWidthPulse * musicWidthPulse;
lane2.scaleX = baseWidthPulse * musicWidthPulse;
// Music-reactive color shifting
var tempo = Math.sin(LK.ticks * 0.2) * 0.5 + 0.5;
var musicTempo = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var colorR = Math.floor((tempo + musicTempo) * 100) % 255;
var colorG = Math.floor(musicTempo * 150) % 255;
var colorTint = 0xFF0000 | colorG << 8 | 0xFF;
lane1.tint = colorTint;
lane2.tint = colorTint;
}
// Update timer - countdown every second (60 frames)
if (LK.ticks % 60 === 0 && timeRemaining > 0) {
timeRemaining--;
timerTxt.setText(timeRemaining.toString());
}
// Check for game over when timer reaches 0
if (timeRemaining <= 0 && gameState === 'playing') {
gameState = 'gameOver';
// Clean up visual key
if (visualKey && visualKey.parent) {
visualKey.destroy();
visualKey = null;
}
// Update final score display
scoreTxt.setText(LK.getScore());
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Hide buttons
for (var i = 0; i < buttons.length; i++) {
buttons[i].visible = false;
}
// Stop all music when game over screen appears
LK.stopMusic();
// Play game ending music after a brief pause
LK.setTimeout(function () {
LK.playMusic('End', {
loop: true
});
}, 500);
// Show game over menu with smooth animations
gameOverTxt.visible = true;
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 0.5;
gameOverTxt.scaleY = 0.5;
finalScoreTxt.setText('Final Score: ' + LK.getScore() + '\nDifficulty: ' + difficulties[currentDifficulty].name);
finalScoreTxt.visible = true;
finalScoreTxt.alpha = 0;
finalScoreTxt.y = finalScoreTxt.y + 50;
gameOverButton.shape.visible = true;
gameOverButton.shape.alpha = 0;
gameOverButton.shape.scaleX = 0;
gameOverButton.shape.scaleY = 0;
gameOverButton.text.visible = true;
gameOverButton.text.alpha = 0;
// Animate game over text
tween(gameOverTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate final score text sliding up
tween(finalScoreTxt, {
alpha: 1,
y: finalScoreTxt.y - 50
}, {
duration: 600,
easing: tween.easeOut,
delay: 400
});
// Animate restart button
tween(gameOverButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 800
});
tween(gameOverButton.text, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
}
// Detect beats and trigger key animations
if (gameState === 'playing') {
detectBeat();
}
// Update visual key if it exists
if (visualKey && visualKey.update && gameState === 'playing') {
visualKey.update();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1624,17 +1624,10 @@
return;
}
// Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
- // Pulsing effect based on notes approaching
- var nearbyNotes = 0;
- for (var t = 0; t < notes.length; t++) {
- var distanceToLine = Math.abs(notes[t].y - timingLineY);
- if (distanceToLine < 200) {
- nearbyNotes++;
- }
- }
- var pulseIntensity = 1 + nearbyNotes * 0.1;
+ // Pulsing effect based on beat intensity
+ var pulseIntensity = 1 + beatIntensity * 0.3;
var basePulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1 * pulseIntensity;
// Add music beat synchronization
var musicPulse = 1;
if (beatIntensity > 0) {
@@ -1646,9 +1639,10 @@
timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.2;
// Music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.05) * 0.5 + 0.5;
var baseColor = 0xFFFFFF;
- if (nearbyNotes > 0) {
+ // Beat-reactive color effects
+ if (beatIntensity > 0.3) {
var colorShift = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
baseColor = 0xFFFFFF + (Math.floor(colorShift * 50) << 16);
}
// Add music-reactive blue/cyan tint