/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, bestCombo: 0 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); self.currentLane = 1; // 0=left, 1=center, 2=right self.targetY = 0; self.bounceHeight = 60; self.bounceSpeed = 0.05; self.bounceTime = 0; self.hasSpeedBoost = false; self.hasSlowMotion = false; self.doubleScoreActive = false; self.effectTimer = 0; self.moveTo = function (laneIndex) { if (laneIndex < 0 || laneIndex > 2) { return; } // Don't move if already in this lane if (self.currentLane === laneIndex) { return; } self.currentLane = laneIndex; var targetX = lanePositions[laneIndex]; // Animate the movement tween(self, { x: targetX }, { duration: 150, easing: tween.easeOut }); }; self.activatePowerup = function (type) { switch (type) { case 'speed': self.hasSpeedBoost = true; self.effectTimer = 300; // 5 seconds at 60fps tween(ballGraphics, { tint: 0xFFFF00 }, { duration: 200 }); break; case 'double': self.doubleScoreActive = true; self.effectTimer = 300; tween(ballGraphics, { tint: 0x00FF00 }, { duration: 200 }); break; case 'slow': self.hasSlowMotion = true; self.effectTimer = 300; tween(ballGraphics, { tint: 0x9900FF }, { duration: 200 }); break; } }; self.update = function () { // Update bounce animation self.bounceTime += self.bounceSpeed * (self.hasSpeedBoost ? 1.5 : self.hasSlowMotion ? 0.5 : 1); var bounceOffset = Math.sin(self.bounceTime) * self.bounceHeight; self.y = self.targetY - bounceOffset; // Update powerup timers if (self.effectTimer > 0) { self.effectTimer--; if (self.effectTimer === 0) { // Reset effects self.hasSpeedBoost = false; self.hasSlowMotion = false; self.doubleScoreActive = false; tween(ballGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } } }; return self; }); var BeatTile = Container.expand(function () { var self = Container.call(this); var tileGraphics = self.attachAsset('beatTile', { anchorX: 0.5, anchorY: 0.5 }); self.lane = 0; self.active = true; self.speed = 5; self.hit = false; self.update = function () { if (!self.active) { return; } self.y += self.speed * (ball.hasSpeedBoost ? 1.5 : ball.hasSlowMotion ? 0.5 : 1); // Check if the tile has gone too far off-screen if (self.y > 2732 + tileGraphics.height) { self.active = false; } }; self.checkHit = function () { if (!self.active || self.hit) { return false; } // Check if ball is in the same lane and close enough in Y position if (self.lane === ball.currentLane) { var distance = Math.abs(self.y - ball.y); if (distance < 80) { self.hit = true; self.active = false; // Animate hit tween(tileGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut }); return true; } } return false; }; self.showMissedIndicator = function () { var missedIndicator = LK.getAsset('missedTile', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.7 }); game.addChild(missedIndicator); tween(missedIndicator, { alpha: 0, scaleX: 1.3, scaleY: 1.3 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { missedIndicator.destroy(); } }); }; return self; }); var Powerup = Container.expand(function () { var self = Container.call(this); self.type = 'speed'; self.active = true; self.speed = 5; self.lane = 0; self.initialize = function (powerupType) { self.type = powerupType; // Different asset based on type var assetId; switch (powerupType) { case 'speed': assetId = 'powerupSpeed'; break; case 'double': assetId = 'powerupDouble'; break; case 'slow': assetId = 'powerupSlow'; break; default: assetId = 'powerupSpeed'; } self.graphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; self.update = function () { if (!self.active) { return; } self.y += self.speed * (ball.hasSpeedBoost ? 1.5 : ball.hasSlowMotion ? 0.5 : 1); self.rotation += 0.03; // Check if collected if (self.lane === ball.currentLane && Math.abs(self.y - ball.y) < 70) { self.collect(); return; } // Check if gone off-screen if (self.y > 2732 + 60) { self.active = false; } }; self.collect = function () { self.active = false; // Animate collection tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut }); // Play powerup collect sound LK.getSound('powerupCollect').play(); // Create explosion effect var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); game.addChild(explosion); tween(explosion, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Apply effect ball.activatePowerup(self.type); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111133 }); /**** * Game Code ****/ // Game constants // Add background image asset var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var lanePositions = [GAME_WIDTH / 4, GAME_WIDTH / 2, 3 * GAME_WIDTH / 4]; var beatGenerationSpeed = 90; // frames between beat tiles var powerupChance = 0.15; // probability of a powerup spawning var difficultyIncreaseRate = 0.0001; // how fast the game speeds up // Game state var beatTimer = 0; var score = 0; var combo = 0; var highestCombo = 0; var gameSpeed = 1; var tiles = []; var powerups = []; var ball; var scoreText; var comboText; var highScoreText; // Set up game background var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2 }); game.addChild(background); // Create lane indicators for (var i = 0; i < 3; i++) { var lane = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0, x: lanePositions[i], y: 0, alpha: 0.3 }); game.addChild(lane); } // Set up game UI scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreText); scoreText.x = 120; // Avoid the top-left 100x100 area scoreText.y = 20; comboText = new Text2('Combo: 0', { size: 80, fill: 0xFFFFFF }); comboText.anchor.set(0, 0); LK.gui.topLeft.addChild(comboText); comboText.x = 120; comboText.y = 110; // High score display highScoreText = new Text2('High Score: ' + storage.highScore, { size: 60, fill: 0xFFCC00 }); highScoreText.anchor.set(1, 0); LK.gui.topRight.addChild(highScoreText); highScoreText.x = -20; highScoreText.y = 20; // Create player ball ball = new Ball(); ball.x = lanePositions[1]; // Start in middle lane ball.y = GAME_HEIGHT - 400; // Position near bottom ball.targetY = ball.y; game.addChild(ball); // Start with some beat tiles generateInitialTiles(); // Start music LK.playMusic('gameMusic', { fade: { start: 0, end: 0.8, duration: 1000 } }); // Touch controls function handleTouch(x, y) { // Determine which third of the screen was tapped if (x < GAME_WIDTH / 3) { ball.moveTo(0); // Left lane } else if (x < 2 * GAME_WIDTH / 3) { ball.moveTo(1); // Middle lane } else { ball.moveTo(2); // Right lane } } game.down = function (x, y, obj) { handleTouch(x, y); }; function generateBeatTile() { var lane = Math.floor(Math.random() * 3); var tile = new BeatTile(); tile.x = lanePositions[lane]; tile.y = -100; // Start above the screen tile.lane = lane; tile.speed *= gameSpeed; game.addChild(tile); tiles.push(tile); // Sometimes spawn a powerup if (Math.random() < powerupChance) { generatePowerup(lane); } } function generatePowerup(lane) { var powerupTypes = ['speed', 'double', 'slow']; var type = powerupTypes[Math.floor(Math.random() * powerupTypes.length)]; var powerup = new Powerup(); powerup.initialize(type); powerup.x = lanePositions[lane]; powerup.y = -200; // Start above the screen powerup.lane = lane; powerup.speed *= gameSpeed; game.addChild(powerup); powerups.push(powerup); } function generateInitialTiles() { // Create a few initial tiles to get started for (var i = 0; i < 5; i++) { var lane = Math.floor(Math.random() * 3); var tile = new BeatTile(); tile.x = lanePositions[lane]; tile.y = -100 - i * 400; // Stagger them tile.lane = lane; tile.speed *= gameSpeed; game.addChild(tile); tiles.push(tile); } } function updateScore(points) { var pointsToAdd = points; // Apply combo multiplier (starting from 1) if (combo > 0) { pointsToAdd = Math.floor(points * (1 + combo * 0.1)); } // Apply double score powerup if (ball.doubleScoreActive) { pointsToAdd *= 2; } score += pointsToAdd; scoreText.setText('Score: ' + score); // Show score popup var scorePopup = new Text2('+' + pointsToAdd, { size: 70, fill: 0xFFFF00 }); scorePopup.anchor.set(0.5, 0.5); scorePopup.x = ball.x; scorePopup.y = ball.y - 100; game.addChild(scorePopup); tween(scorePopup, { alpha: 0, y: scorePopup.y - 100 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { scorePopup.destroy(); } }); } game.update = function () { // Update game speed gameSpeed += difficultyIncreaseRate; // Generate new beat tiles beatTimer++; if (beatTimer >= beatGenerationSpeed) { beatTimer = 0; generateBeatTile(); // Slightly decrease time between beats as game progresses beatGenerationSpeed = Math.max(40, beatGenerationSpeed - 0.1); } // Update all game objects for (var i = tiles.length - 1; i >= 0; i--) { var tile = tiles[i]; tile.update(); // Check for hits if (tile.active && !tile.hit && tile.checkHit()) { // Increase combo combo++; if (combo > highestCombo) { highestCombo = combo; } comboText.setText("Combo: " + combo); // Play tile hit sound LK.getSound('tileHit').play(); // Update score updateScore(10); } // Clean up inactive tiles if (!tile.active) { tiles.splice(i, 1); } } // Update powerups for (var j = powerups.length - 1; j >= 0; j--) { var powerup = powerups[j]; powerup.update(); if (!powerup.active) { powerups.splice(j, 1); } } // Update player ball.update(); // Check for game over if (score > 1000) { // Save high score if (score > storage.highScore) { storage.highScore = score; } // Save best combo if (highestCombo > storage.bestCombo) { storage.bestCombo = highestCombo; } LK.showYouWin(); } // Play comboBreak sound if combo is reset if (combo === 0 && highestCombo > 0) { LK.getSound('comboBreak').play(); } // Play beatMiss sound if a tile is missed for (var i = tiles.length - 1; i >= 0; i--) { var tile = tiles[i]; if (!tile.active && !tile.hit) { LK.getSound('beatMiss').play(); } } }; // Clean up function if game is destroyed game.destroy = function () { // Stop any tweens for (var i = 0; i < tiles.length; i++) { tween.stop(tiles[i]); } for (var j = 0; j < powerups.length; j++) { tween.stop(powerups[j]); } tween.stop(ball); // Stop music LK.stopMusic(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
bestCombo: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
self.currentLane = 1; // 0=left, 1=center, 2=right
self.targetY = 0;
self.bounceHeight = 60;
self.bounceSpeed = 0.05;
self.bounceTime = 0;
self.hasSpeedBoost = false;
self.hasSlowMotion = false;
self.doubleScoreActive = false;
self.effectTimer = 0;
self.moveTo = function (laneIndex) {
if (laneIndex < 0 || laneIndex > 2) {
return;
}
// Don't move if already in this lane
if (self.currentLane === laneIndex) {
return;
}
self.currentLane = laneIndex;
var targetX = lanePositions[laneIndex];
// Animate the movement
tween(self, {
x: targetX
}, {
duration: 150,
easing: tween.easeOut
});
};
self.activatePowerup = function (type) {
switch (type) {
case 'speed':
self.hasSpeedBoost = true;
self.effectTimer = 300; // 5 seconds at 60fps
tween(ballGraphics, {
tint: 0xFFFF00
}, {
duration: 200
});
break;
case 'double':
self.doubleScoreActive = true;
self.effectTimer = 300;
tween(ballGraphics, {
tint: 0x00FF00
}, {
duration: 200
});
break;
case 'slow':
self.hasSlowMotion = true;
self.effectTimer = 300;
tween(ballGraphics, {
tint: 0x9900FF
}, {
duration: 200
});
break;
}
};
self.update = function () {
// Update bounce animation
self.bounceTime += self.bounceSpeed * (self.hasSpeedBoost ? 1.5 : self.hasSlowMotion ? 0.5 : 1);
var bounceOffset = Math.sin(self.bounceTime) * self.bounceHeight;
self.y = self.targetY - bounceOffset;
// Update powerup timers
if (self.effectTimer > 0) {
self.effectTimer--;
if (self.effectTimer === 0) {
// Reset effects
self.hasSpeedBoost = false;
self.hasSlowMotion = false;
self.doubleScoreActive = false;
tween(ballGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
};
return self;
});
var BeatTile = Container.expand(function () {
var self = Container.call(this);
var tileGraphics = self.attachAsset('beatTile', {
anchorX: 0.5,
anchorY: 0.5
});
self.lane = 0;
self.active = true;
self.speed = 5;
self.hit = false;
self.update = function () {
if (!self.active) {
return;
}
self.y += self.speed * (ball.hasSpeedBoost ? 1.5 : ball.hasSlowMotion ? 0.5 : 1);
// Check if the tile has gone too far off-screen
if (self.y > 2732 + tileGraphics.height) {
self.active = false;
}
};
self.checkHit = function () {
if (!self.active || self.hit) {
return false;
}
// Check if ball is in the same lane and close enough in Y position
if (self.lane === ball.currentLane) {
var distance = Math.abs(self.y - ball.y);
if (distance < 80) {
self.hit = true;
self.active = false;
// Animate hit
tween(tileGraphics, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut
});
return true;
}
}
return false;
};
self.showMissedIndicator = function () {
var missedIndicator = LK.getAsset('missedTile', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.7
});
game.addChild(missedIndicator);
tween(missedIndicator, {
alpha: 0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
missedIndicator.destroy();
}
});
};
return self;
});
var Powerup = Container.expand(function () {
var self = Container.call(this);
self.type = 'speed';
self.active = true;
self.speed = 5;
self.lane = 0;
self.initialize = function (powerupType) {
self.type = powerupType;
// Different asset based on type
var assetId;
switch (powerupType) {
case 'speed':
assetId = 'powerupSpeed';
break;
case 'double':
assetId = 'powerupDouble';
break;
case 'slow':
assetId = 'powerupSlow';
break;
default:
assetId = 'powerupSpeed';
}
self.graphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.update = function () {
if (!self.active) {
return;
}
self.y += self.speed * (ball.hasSpeedBoost ? 1.5 : ball.hasSlowMotion ? 0.5 : 1);
self.rotation += 0.03;
// Check if collected
if (self.lane === ball.currentLane && Math.abs(self.y - ball.y) < 70) {
self.collect();
return;
}
// Check if gone off-screen
if (self.y > 2732 + 60) {
self.active = false;
}
};
self.collect = function () {
self.active = false;
// Animate collection
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut
});
// Play powerup collect sound
LK.getSound('powerupCollect').play();
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
tween(explosion, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Apply effect
ball.activatePowerup(self.type);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111133
});
/****
* Game Code
****/
// Game constants
// Add background image asset
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var lanePositions = [GAME_WIDTH / 4, GAME_WIDTH / 2, 3 * GAME_WIDTH / 4];
var beatGenerationSpeed = 90; // frames between beat tiles
var powerupChance = 0.15; // probability of a powerup spawning
var difficultyIncreaseRate = 0.0001; // how fast the game speeds up
// Game state
var beatTimer = 0;
var score = 0;
var combo = 0;
var highestCombo = 0;
var gameSpeed = 1;
var tiles = [];
var powerups = [];
var ball;
var scoreText;
var comboText;
var highScoreText;
// Set up game background
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2
});
game.addChild(background);
// Create lane indicators
for (var i = 0; i < 3; i++) {
var lane = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0,
x: lanePositions[i],
y: 0,
alpha: 0.3
});
game.addChild(lane);
}
// Set up game UI
scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 120; // Avoid the top-left 100x100 area
scoreText.y = 20;
comboText = new Text2('Combo: 0', {
size: 80,
fill: 0xFFFFFF
});
comboText.anchor.set(0, 0);
LK.gui.topLeft.addChild(comboText);
comboText.x = 120;
comboText.y = 110;
// High score display
highScoreText = new Text2('High Score: ' + storage.highScore, {
size: 60,
fill: 0xFFCC00
});
highScoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(highScoreText);
highScoreText.x = -20;
highScoreText.y = 20;
// Create player ball
ball = new Ball();
ball.x = lanePositions[1]; // Start in middle lane
ball.y = GAME_HEIGHT - 400; // Position near bottom
ball.targetY = ball.y;
game.addChild(ball);
// Start with some beat tiles
generateInitialTiles();
// Start music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.8,
duration: 1000
}
});
// Touch controls
function handleTouch(x, y) {
// Determine which third of the screen was tapped
if (x < GAME_WIDTH / 3) {
ball.moveTo(0); // Left lane
} else if (x < 2 * GAME_WIDTH / 3) {
ball.moveTo(1); // Middle lane
} else {
ball.moveTo(2); // Right lane
}
}
game.down = function (x, y, obj) {
handleTouch(x, y);
};
function generateBeatTile() {
var lane = Math.floor(Math.random() * 3);
var tile = new BeatTile();
tile.x = lanePositions[lane];
tile.y = -100; // Start above the screen
tile.lane = lane;
tile.speed *= gameSpeed;
game.addChild(tile);
tiles.push(tile);
// Sometimes spawn a powerup
if (Math.random() < powerupChance) {
generatePowerup(lane);
}
}
function generatePowerup(lane) {
var powerupTypes = ['speed', 'double', 'slow'];
var type = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
var powerup = new Powerup();
powerup.initialize(type);
powerup.x = lanePositions[lane];
powerup.y = -200; // Start above the screen
powerup.lane = lane;
powerup.speed *= gameSpeed;
game.addChild(powerup);
powerups.push(powerup);
}
function generateInitialTiles() {
// Create a few initial tiles to get started
for (var i = 0; i < 5; i++) {
var lane = Math.floor(Math.random() * 3);
var tile = new BeatTile();
tile.x = lanePositions[lane];
tile.y = -100 - i * 400; // Stagger them
tile.lane = lane;
tile.speed *= gameSpeed;
game.addChild(tile);
tiles.push(tile);
}
}
function updateScore(points) {
var pointsToAdd = points;
// Apply combo multiplier (starting from 1)
if (combo > 0) {
pointsToAdd = Math.floor(points * (1 + combo * 0.1));
}
// Apply double score powerup
if (ball.doubleScoreActive) {
pointsToAdd *= 2;
}
score += pointsToAdd;
scoreText.setText('Score: ' + score);
// Show score popup
var scorePopup = new Text2('+' + pointsToAdd, {
size: 70,
fill: 0xFFFF00
});
scorePopup.anchor.set(0.5, 0.5);
scorePopup.x = ball.x;
scorePopup.y = ball.y - 100;
game.addChild(scorePopup);
tween(scorePopup, {
alpha: 0,
y: scorePopup.y - 100
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
scorePopup.destroy();
}
});
}
game.update = function () {
// Update game speed
gameSpeed += difficultyIncreaseRate;
// Generate new beat tiles
beatTimer++;
if (beatTimer >= beatGenerationSpeed) {
beatTimer = 0;
generateBeatTile();
// Slightly decrease time between beats as game progresses
beatGenerationSpeed = Math.max(40, beatGenerationSpeed - 0.1);
}
// Update all game objects
for (var i = tiles.length - 1; i >= 0; i--) {
var tile = tiles[i];
tile.update();
// Check for hits
if (tile.active && !tile.hit && tile.checkHit()) {
// Increase combo
combo++;
if (combo > highestCombo) {
highestCombo = combo;
}
comboText.setText("Combo: " + combo);
// Play tile hit sound
LK.getSound('tileHit').play();
// Update score
updateScore(10);
}
// Clean up inactive tiles
if (!tile.active) {
tiles.splice(i, 1);
}
}
// Update powerups
for (var j = powerups.length - 1; j >= 0; j--) {
var powerup = powerups[j];
powerup.update();
if (!powerup.active) {
powerups.splice(j, 1);
}
}
// Update player
ball.update();
// Check for game over
if (score > 1000) {
// Save high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Save best combo
if (highestCombo > storage.bestCombo) {
storage.bestCombo = highestCombo;
}
LK.showYouWin();
}
// Play comboBreak sound if combo is reset
if (combo === 0 && highestCombo > 0) {
LK.getSound('comboBreak').play();
}
// Play beatMiss sound if a tile is missed
for (var i = tiles.length - 1; i >= 0; i--) {
var tile = tiles[i];
if (!tile.active && !tile.hit) {
LK.getSound('beatMiss').play();
}
}
};
// Clean up function if game is destroyed
game.destroy = function () {
// Stop any tweens
for (var i = 0; i < tiles.length; i++) {
tween.stop(tiles[i]);
}
for (var j = 0; j < powerups.length; j++) {
tween.stop(powerups[j]);
}
tween.stop(ball);
// Stop music
LK.stopMusic();
};
Design: Square or circular glowing pads that react when hit. Effect: A pulse effect when the ball lands perfectly. Variations: Different colors per beat type (e.g., blue for normal, red for speed boosts).. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Design: A faded-out or cracked tile to indicate a miss. Effect: A quick screen shake or particle effect when touched.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Design: A dynamic, glowing path that scrolls forward. Style Options: Cyberpunk neon rails Floating musical staff lines A galaxy wave track. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
PowerUpDouble ⭐ Golden glow tile Score x2 for 5 sec. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
PowerUpSlow ⏳ Blue time freeze tile Slows music briefly. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
PowerUpSpeed ⚡ Red speed trail tile Speeds up for bonus. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows