User prompt
now lets make difficulty selector. easy to hard
User prompt
at start screen "tap to play" is on the orb. take it little bit down
User prompt
change the orb to a speaker and put an effect that beating with waves ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
can you change orb to G sign
User prompt
start screen background image doesnt fit the screen please check it
User prompt
change the color and type of start screen writings
User prompt
add a futuristic background for start screen and game screen. but both different
User prompt
change the game name. just first line delete face bla bla thing
User prompt
not bad for begining. let's make a start screen
User prompt
still there is a problem. loading screen freezed
User prompt
make facekit optional. if there isn't camera go on with mouse control ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
i added assets. what's wrong?
Code edit (1 edits merged)
Please save this source code
User prompt
Rhythm Sync - Face-Controlled Beat Catcher
Initial prompt
/**** Assets ****/ LK.init.shape('orb', {width: 80, height: 80, color: 0x00ffff, shape: 'ellipse'}); LK.init.shape('note', {width: 40, height: 40, color: 0xff00ff, shape: 'ellipse'}); LK.init.sound('pulse'); LK.init.music('mainTrack'); /**** Plugins ****/ var facekit = LK.import('@upit/facekit.v1'); var tween = LK.import('@upit/tween.v1'); /**** Variables ****/ var orb; var notes = []; var score = 0; var onBeat = false; var beatTimer = 0; var beatInterval = 30; var beatWindow = 8; var scoreDisplay; /**** Classes ****/ var Orb = Container.expand(function () { var self = Container.call(this); var g = self.attachAsset('orb', {anchorX: 0.5, anchorY: 0.5}); self.update = function () { if (onBeat) { g.tint = 0x00ffff; tween(g, {scaleX: 1.2, scaleY: 1.2}, {duration: 100}); tween(g, {scaleX: 1.0, scaleY: 1.0}, {duration: 200}); } else { g.tint = 0x004488; } }; return self; }); var Note = Container.expand(function () { var self = Container.call(this); var g = self.attachAsset('note', {anchorX: 0.5, anchorY: 0.5}); self.update = function () { if (onBeat) { g.alpha = 1; } else { g.alpha = 0.6; } if (orb.intersects(self)) { LK.getSound('pulse').play(); score += 10; updateScore(); tween(self, {scaleX: 2, scaleY: 2, alpha: 0}, { duration: 200, onFinish: function () { self.destroy(); notes.splice(notes.indexOf(self), 1); } }); } }; return self; }); /**** Game Setup ****/ var game = new LK.Game({backgroundColor: 0x001122}); orb = new Orb(); orb.x = 1024; orb.y = 1200; game.addChild(orb); scoreDisplay = new Text2('Score: 0', {size: 60, fill: 0xffffff}); scoreDisplay.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreDisplay); facekit.init(); // Aktif etmek istemezsen bu satırı yorum satırı yap function updateScore() { scoreDisplay.setText('Score: ' + score); } /**** Input ****/ game.down = function (x, y) { orb.x = x; orb.y = y; }; game.drag = function (x, y) { orb.x = x; orb.y = y; }; /**** Loop ****/ game.update = function () { // Beat hesaplama beatTimer++; var beatPhase = beatTimer % beatInterval; onBeat = beatPhase < beatWindow; // Beat başladığında nota üret if (beatPhase === 0) { var n = new Note(); n.x = Math.random() * 2000; n.y = Math.random() * 2000; notes.push(n); game.addChild(n); } // FaceKit durumları (sadece hazır olsun diye) if (facekit.smile) game.setBackgroundColor(0x224455); else if (facekit.mouthOpen) game.setBackgroundColor(0x552244); else if (facekit.frown) game.setBackgroundColor(0x331111); else game.setBackgroundColor(0x001122); }; /**** Müzik ****/ LK.playMusic('mainTrack');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
easyScores: [],
mediumScores: [],
hardScores: []
});
/****
* Classes
****/
var Note = Container.expand(function () {
var self = Container.call(this);
var noteGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
self.isVisible = false;
self.isCollected = false;
noteGraphics.alpha = 0.3;
self.pulse = function () {
noteGraphics.alpha = 1.0;
self.isVisible = true;
tween(noteGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(noteGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
}
});
};
self.fadeOut = function () {
self.isVisible = false;
tween(noteGraphics, {
alpha: 0.3
}, {
duration: 200
});
};
return self;
});
var Orb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
// Create wave effect circles
var wave1 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 1.5,
scaleY: 1.5
});
var wave2 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
scaleX: 2.0,
scaleY: 2.0
});
var wave3 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1,
scaleX: 2.5,
scaleY: 2.5
});
self.addChild(wave3);
self.addChild(wave2);
self.addChild(wave1);
self.addChild(orbGraphics);
// Start continuous wave animation
self.startWaves = function () {
// Wave 1 animation
tween(wave1, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave1.scaleX = 1.5;
wave1.scaleY = 1.5;
wave1.alpha = 0.3;
self.startWaves();
}
});
// Wave 2 animation (delayed)
LK.setTimeout(function () {
tween(wave2, {
scaleX: 3.5,
scaleY: 3.5,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave2.scaleX = 2.0;
wave2.scaleY = 2.0;
wave2.alpha = 0.2;
}
});
}, 300);
// Wave 3 animation (more delayed)
LK.setTimeout(function () {
tween(wave3, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave3.scaleX = 2.5;
wave3.scaleY = 2.5;
wave3.alpha = 0.1;
}
});
}, 600);
};
// Start the wave effect immediately
self.startWaves();
self.isGlowing = false;
self.startGlow = function () {
if (!self.isGlowing) {
self.isGlowing = true;
tween(orbGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200
});
}
};
self.stopGlow = function () {
if (self.isGlowing) {
self.isGlowing = false;
tween(orbGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state management
var gameState = 'start'; // 'start', 'playing', 'gameOver'
var difficulty = 'medium'; // 'easy', 'medium', 'hard'
var difficultySelected = false;
var startScreen = null;
var startBackground = null;
var gameBackground = null;
var orb = null;
var notes = [];
var beatWindow = false;
var beatWindowCounter = 0;
var beatCounter = 0;
var dragNode = null;
var gameStartTick = 0;
var gameTimer = 0;
var gameTimerMax = 1320; // 22 seconds at 60 FPS
// Background colors for different expressions
var neutralColor = 0x1a1a2e;
var smileColor = 0x4a90e2;
var frownColor = 0x8b0000;
var mouthOpenColor = 0x9b59b6;
var currentBgColor = neutralColor;
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
var timerTxt = null;
function showScoreboard() {
gameState = 'scoreboard';
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Create scoreboard container
var scoreboardScreen = new Container();
game.addChild(scoreboardScreen);
// Background
var scoreboardBg = scoreboardScreen.attachAsset('startBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.33,
alpha: 0.8
});
// Title
var titleText = new Text2('HIGH SCORES', {
size: 100,
fill: 0xff00ff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
scoreboardScreen.addChild(titleText);
// Difficulty sections
var difficulties = ['EASY', 'MEDIUM', 'HARD'];
var storageKeys = ['easyScores', 'mediumScores', 'hardScores'];
var colors = [0x00ff00, 0xffff00, 0xff0000];
for (var d = 0; d < 3; d++) {
var difficultyTitle = new Text2(difficulties[d], {
size: 60,
fill: colors[d]
});
difficultyTitle.anchor.set(0.5, 0.5);
difficultyTitle.x = 350 + d * 350;
difficultyTitle.y = 600;
scoreboardScreen.addChild(difficultyTitle);
// Get scores for this difficulty
var scores = storage[storageKeys[d]] || [];
// Display top 5 scores
for (var s = 0; s < 5; s++) {
var scoreText = scores[s] ? scores[s].toString() : '-';
var scoreDisplay = new Text2(s + 1 + '. ' + scoreText, {
size: 40,
fill: 0xffffff
});
scoreDisplay.anchor.set(0.5, 0.5);
scoreDisplay.x = 350 + d * 350;
scoreDisplay.y = 700 + s * 60;
scoreboardScreen.addChild(scoreDisplay);
}
}
// Back button
var backText = new Text2('TAP TO CONTINUE', {
size: 60,
fill: 0x00ff88
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2200;
scoreboardScreen.addChild(backText);
// Store reference for cleanup
game.scoreboardScreen = scoreboardScreen;
}
function getHighestScore() {
var difficulties = ['easy', 'medium', 'hard'];
var highestScore = 0;
var highestDifficulty = 'none';
for (var d = 0; d < difficulties.length; d++) {
var storageKey = difficulties[d] + 'Scores';
var scores = storage[storageKey] || [];
if (scores.length > 0 && scores[0] > highestScore) {
highestScore = scores[0];
highestDifficulty = difficulties[d].toUpperCase();
}
}
return {
score: highestScore,
difficulty: highestDifficulty
};
}
function saveScore(score, difficulty) {
var storageKey = difficulty + 'Scores';
var scores = storage[storageKey] || [];
// Add new score
scores.push(score);
// Sort in descending order
scores.sort(function (a, b) {
return b - a;
});
// Keep only top 5
if (scores.length > 5) {
scores = scores.slice(0, 5);
}
// Save back to storage
storage[storageKey] = scores;
}
function createStartScreen() {
startScreen = new Container();
game.addChild(startScreen);
// Create animated start background
startBackground = startScreen.attachAsset('startBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.33
});
// Create multiple layers for depth effect
var bgLayer1 = LK.getAsset('startBg', {
anchorX: 0,
anchorY: 0,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.3
});
bgLayer1.x = -100;
bgLayer1.y = -100;
startScreen.addChild(bgLayer1);
var bgLayer2 = LK.getAsset('startBg', {
anchorX: 0,
anchorY: 0,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.5
});
bgLayer2.x = 200;
bgLayer2.y = 200;
startScreen.addChild(bgLayer2);
// Animate background layers
tween(bgLayer1, {
rotation: Math.PI * 2
}, {
duration: 20000,
repeat: -1
});
tween(bgLayer2, {
rotation: -Math.PI * 2
}, {
duration: 15000,
repeat: -1
});
// Title
var titleText = new Text2('RHYTHM SYNC', {
size: 120,
fill: 0xff00ff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
startScreen.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Beat Catcher', {
size: 60,
fill: 0x00ff88
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 920;
startScreen.addChild(subtitleText);
// Play button background
var playButtonBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
playButtonBg.x = 1024;
playButtonBg.y = 1200;
startScreen.addChild(playButtonBg);
// Play button text
var playText = new Text2('TAP TO PLAY', {
size: 80,
fill: 0xffff00
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1300;
startScreen.addChild(playText);
// Difficulty selector background
var selectorBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8.0,
scaleY: 1.5
});
selectorBg.x = 1024;
selectorBg.y = 1520;
selectorBg.alpha = 0.3;
startScreen.addChild(selectorBg);
// Difficulty button backgrounds
var easyBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2
});
easyBtnBg.x = 724;
easyBtnBg.y = 1520;
easyBtnBg.alpha = difficulty === 'easy' ? 0.8 : 0.3;
startScreen.addChild(easyBtnBg);
var mediumBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
tint: 0xffffff
});
mediumBtnBg.x = 1024;
mediumBtnBg.y = 1520;
mediumBtnBg.alpha = difficulty === 'medium' ? 0.8 : 0.3;
startScreen.addChild(mediumBtnBg);
var hardBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2
});
hardBtnBg.x = 1324;
hardBtnBg.y = 1520;
hardBtnBg.alpha = difficulty === 'hard' ? 0.8 : 0.3;
startScreen.addChild(hardBtnBg);
// Difficulty buttons
var easyBtn = new Text2('EASY', {
size: 80,
fill: difficulty === 'easy' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
easyBtn.anchor.set(0.5, 0.5);
easyBtn.x = 724;
easyBtn.y = 1520;
startScreen.addChild(easyBtn);
var mediumBtn = new Text2('MEDIUM', {
size: 80,
fill: difficulty === 'medium' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
mediumBtn.anchor.set(0.5, 0.5);
mediumBtn.x = 1024;
mediumBtn.y = 1520;
startScreen.addChild(mediumBtn);
var hardBtn = new Text2('HARD', {
size: 80,
fill: difficulty === 'hard' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
hardBtn.anchor.set(0.5, 0.5);
hardBtn.x = 1324;
hardBtn.y = 1520;
startScreen.addChild(hardBtn);
// Selection indicator
var selectionIndicator = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 1.0
});
selectionIndicator.alpha = 0.5;
selectionIndicator.x = difficulty === 'easy' ? 724 : difficulty === 'medium' ? 1024 : 1324;
selectionIndicator.y = 1520;
startScreen.addChild(selectionIndicator);
// Store buttons for easy access
startScreen.easyBtn = easyBtn;
startScreen.mediumBtn = mediumBtn;
startScreen.hardBtn = hardBtn;
startScreen.easyBtnBg = easyBtnBg;
startScreen.mediumBtnBg = mediumBtnBg;
startScreen.hardBtnBg = hardBtnBg;
startScreen.selectionIndicator = selectionIndicator;
startScreen.difficultySelected = true;
// Instructions
var instructText = new Text2('Drag the orb to collect notes on the beat!', {
size: 50,
fill: 0xff6600
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1600;
startScreen.addChild(instructText);
// Scoreboard button
var scoreboardBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 1.0
});
scoreboardBg.x = 1024;
scoreboardBg.y = 1750;
scoreboardBg.alpha = 0.4;
startScreen.addChild(scoreboardBg);
var scoreboardText = new Text2('HIGH SCORES', {
size: 60,
fill: 0x00ffff
});
scoreboardText.anchor.set(0.5, 0.5);
scoreboardText.x = 1024;
scoreboardText.y = 1750;
startScreen.addChild(scoreboardText);
// Add highest score display under scoreboard button
var highestData = getHighestScore();
var highestScoreText = '';
if (highestData.score > 0) {
highestScoreText = 'Best: ' + highestData.score + ' (' + highestData.difficulty + ')';
} else {
highestScoreText = 'No scores yet';
}
var bestScoreDisplay = new Text2(highestScoreText, {
size: 40,
fill: 0xffaa00
});
bestScoreDisplay.anchor.set(0.5, 0.5);
bestScoreDisplay.x = 1024;
bestScoreDisplay.y = 1800;
startScreen.addChild(bestScoreDisplay);
// Animate title
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
yoyo: true,
repeat: -1
});
// Play background music for start screen
LK.playMusic('bgmusic');
}
function startGame() {
gameState = 'playing';
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Create animated game background
gameBackground = game.attachAsset('gameBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.54
});
// Create floating elements for atmosphere
var floatingElements = [];
for (var i = 0; i < 8; i++) {
var element = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
element.alpha = 0.1;
element.x = Math.random() * 2048;
element.y = Math.random() * 2732;
game.addChild(element);
floatingElements.push(element);
// Animate floating elements
tween(element, {
y: element.y + (Math.random() * 400 - 200),
x: element.x + (Math.random() * 400 - 200),
alpha: 0.3
}, {
duration: 3000 + Math.random() * 2000,
yoyo: true,
repeat: -1
});
}
// Create orb
orb = game.addChild(new Orb());
orb.x = 1024;
orb.y = 1366;
// Reset beat timing to sync with music start
gameStartTick = LK.ticks;
beatCounter = 0;
gameTimer = 0;
// Create timer display for game
timerTxt = new Text2('22', {
size: 80,
fill: 0xFFFF00
});
timerTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timerTxt);
timerTxt.x = -50;
timerTxt.y = 50;
// Start music based on difficulty (this will automatically stop the current bgmusic)
var musicTrack = difficulty === 'easy' ? 'easyMusic' : difficulty === 'medium' ? 'mediumMusic' : 'hardMusic';
LK.playMusic(musicTrack);
}
function spawnNote() {
var note = new Note();
note.x = Math.random() * 1848 + 100; // Keep notes within screen bounds
note.y = Math.random() * 2532 + 100;
notes.push(note);
game.addChild(note);
}
function updateBackground() {
var targetColor = neutralColor;
if (facekitAvailable) {
if (facekit.mouthOpen) {
targetColor = mouthOpenColor;
} else if (facekit.smile) {
targetColor = smileColor;
} else if (facekit.frown) {
targetColor = frownColor;
}
}
if (targetColor !== currentBgColor) {
currentBgColor = targetColor;
game.setBackgroundColor(targetColor);
}
}
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (gameState === 'start') {
// Check if clicked on difficulty buttons
if (startScreen && y >= 1480 && y <= 1560) {
if (x >= 650 && x <= 800) {
// Easy button clicked
difficulty = 'easy';
difficultySelected = true;
startScreen.easyBtn.fill = 0x000000;
startScreen.mediumBtn.fill = 0xffffff;
startScreen.hardBtn.fill = 0xffffff;
startScreen.easyBtnBg.alpha = 0.8;
startScreen.mediumBtnBg.alpha = 0.3;
startScreen.hardBtnBg.alpha = 0.3;
startScreen.selectionIndicator.x = 724;
} else if (x >= 950 && x <= 1100) {
// Medium button clicked
difficulty = 'medium';
difficultySelected = true;
startScreen.easyBtn.fill = 0xffffff;
startScreen.mediumBtn.fill = 0x000000;
startScreen.hardBtn.fill = 0xffffff;
startScreen.easyBtnBg.alpha = 0.3;
startScreen.mediumBtnBg.alpha = 0.8;
startScreen.hardBtnBg.alpha = 0.3;
startScreen.selectionIndicator.x = 1024;
} else if (x >= 1250 && x <= 1400) {
// Hard button clicked
difficulty = 'hard';
difficultySelected = true;
startScreen.easyBtn.fill = 0xffffff;
startScreen.mediumBtn.fill = 0xffffff;
startScreen.hardBtn.fill = 0x000000;
startScreen.easyBtnBg.alpha = 0.3;
startScreen.mediumBtnBg.alpha = 0.3;
startScreen.hardBtnBg.alpha = 0.8;
startScreen.selectionIndicator.x = 1324;
}
} else if (y >= 1700 && y <= 1800 && x >= 800 && x <= 1250) {
// Scoreboard button clicked
showScoreboard();
} else if (difficultySelected) {
// Start game only if difficulty is selected
startGame();
}
} else if (gameState === 'scoreboard') {
// Return to start screen from scoreboard
if (game.scoreboardScreen) {
game.scoreboardScreen.destroy();
game.scoreboardScreen = null;
}
gameState = 'start';
createStartScreen();
} else if (gameState === 'scoreboard') {
// Return to start screen from scoreboard
if (game.scoreboardScreen) {
game.scoreboardScreen.destroy();
game.scoreboardScreen = null;
}
gameState = 'start';
createStartScreen();
} else if (gameState === 'playing' && orb) {
dragNode = orb;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (gameState === 'playing') {
dragNode = null;
}
};
game.update = function () {
if (gameState === 'playing') {
// Beat timing based on ticks for stable synchronization
var beatInterval = difficulty === 'easy' ? 45 : difficulty === 'medium' ? 30 : 20;
var beatWindowDuration = difficulty === 'easy' ? 12 : difficulty === 'medium' ? 8 : 5;
var spawnChance = difficulty === 'easy' ? 0.5 : difficulty === 'medium' ? 0.7 : 0.9;
// Check if we've hit a new beat using ticks since game started
var ticksSinceStart = LK.ticks - gameStartTick;
if (ticksSinceStart % beatInterval === 0 && ticksSinceStart > 0) {
beatCounter++;
// Start beat window
beatWindow = true;
beatWindowCounter = 0;
// Pulse all notes
for (var i = 0; i < notes.length; i++) {
notes[i].pulse();
}
// Glow orb during beat window
if (orb) {
orb.startGlow();
}
// Spawn new note based on difficulty
if (Math.random() < spawnChance) {
spawnNote();
}
}
// Manage beat window duration based on difficulty
if (beatWindow) {
beatWindowCounter++;
if (beatWindowCounter >= beatWindowDuration) {
beatWindow = false;
if (orb) {
orb.stopGlow();
}
// Fade out notes after beat window
for (var i = 0; i < notes.length; i++) {
notes[i].fadeOut();
}
}
}
// Check for note collection
if (orb) {
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (!note.isCollected && orb.intersects(note)) {
if (beatWindow && note.isVisible) {
// Perfect timing - full points
LK.setScore(LK.getScore() + 10);
LK.getSound('pulse').play();
} else if (note.isVisible) {
// Good timing - half points
LK.setScore(LK.getScore() + 5);
LK.getSound('pulse').play();
}
note.isCollected = true;
note.destroy();
notes.splice(i, 1);
scoreTxt.setText(LK.getScore());
}
}
}
// Update timer display
if (timerTxt) {
var timeLeft = Math.ceil((gameTimerMax - gameTimer) / 60);
timerTxt.setText(timeLeft.toString());
}
// Clean up old notes that haven't been collected
if (LK.ticks % 180 === 0) {
// Every 3 seconds
for (var i = notes.length - 1; i >= 0; i--) {
if (Math.random() < 0.3) {
// 30% chance to remove old notes
notes[i].destroy();
notes.splice(i, 1);
}
}
}
// Update background based on facial expressions (if camera available)
if (facekitAvailable) {
updateBackground();
}
// Update game timer
gameTimer++;
// End game when timer reaches maximum
if (gameTimer >= gameTimerMax) {
// Save the score before showing game over
saveScore(LK.getScore(), difficulty);
LK.showGameOver();
}
// Win condition at 500 points
if (LK.getScore() >= 500) {
// Save the score before showing win screen
saveScore(LK.getScore(), difficulty);
LK.showYouWin();
}
}
};
// Initialize facekit for facial expression detection if camera is available
var facekitAvailable = false;
// Create start screen
createStartScreen(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
easyScores: [],
mediumScores: [],
hardScores: []
});
/****
* Classes
****/
var Note = Container.expand(function () {
var self = Container.call(this);
var noteGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
self.isVisible = false;
self.isCollected = false;
noteGraphics.alpha = 0.3;
self.pulse = function () {
noteGraphics.alpha = 1.0;
self.isVisible = true;
tween(noteGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(noteGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
}
});
};
self.fadeOut = function () {
self.isVisible = false;
tween(noteGraphics, {
alpha: 0.3
}, {
duration: 200
});
};
return self;
});
var Orb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
// Create wave effect circles
var wave1 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 1.5,
scaleY: 1.5
});
var wave2 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
scaleX: 2.0,
scaleY: 2.0
});
var wave3 = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1,
scaleX: 2.5,
scaleY: 2.5
});
self.addChild(wave3);
self.addChild(wave2);
self.addChild(wave1);
self.addChild(orbGraphics);
// Start continuous wave animation
self.startWaves = function () {
// Wave 1 animation
tween(wave1, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave1.scaleX = 1.5;
wave1.scaleY = 1.5;
wave1.alpha = 0.3;
self.startWaves();
}
});
// Wave 2 animation (delayed)
LK.setTimeout(function () {
tween(wave2, {
scaleX: 3.5,
scaleY: 3.5,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave2.scaleX = 2.0;
wave2.scaleY = 2.0;
wave2.alpha = 0.2;
}
});
}, 300);
// Wave 3 animation (more delayed)
LK.setTimeout(function () {
tween(wave3, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
wave3.scaleX = 2.5;
wave3.scaleY = 2.5;
wave3.alpha = 0.1;
}
});
}, 600);
};
// Start the wave effect immediately
self.startWaves();
self.isGlowing = false;
self.startGlow = function () {
if (!self.isGlowing) {
self.isGlowing = true;
tween(orbGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200
});
}
};
self.stopGlow = function () {
if (self.isGlowing) {
self.isGlowing = false;
tween(orbGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state management
var gameState = 'start'; // 'start', 'playing', 'gameOver'
var difficulty = 'medium'; // 'easy', 'medium', 'hard'
var difficultySelected = false;
var startScreen = null;
var startBackground = null;
var gameBackground = null;
var orb = null;
var notes = [];
var beatWindow = false;
var beatWindowCounter = 0;
var beatCounter = 0;
var dragNode = null;
var gameStartTick = 0;
var gameTimer = 0;
var gameTimerMax = 1320; // 22 seconds at 60 FPS
// Background colors for different expressions
var neutralColor = 0x1a1a2e;
var smileColor = 0x4a90e2;
var frownColor = 0x8b0000;
var mouthOpenColor = 0x9b59b6;
var currentBgColor = neutralColor;
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
var timerTxt = null;
function showScoreboard() {
gameState = 'scoreboard';
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Create scoreboard container
var scoreboardScreen = new Container();
game.addChild(scoreboardScreen);
// Background
var scoreboardBg = scoreboardScreen.attachAsset('startBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.33,
alpha: 0.8
});
// Title
var titleText = new Text2('HIGH SCORES', {
size: 100,
fill: 0xff00ff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
scoreboardScreen.addChild(titleText);
// Difficulty sections
var difficulties = ['EASY', 'MEDIUM', 'HARD'];
var storageKeys = ['easyScores', 'mediumScores', 'hardScores'];
var colors = [0x00ff00, 0xffff00, 0xff0000];
for (var d = 0; d < 3; d++) {
var difficultyTitle = new Text2(difficulties[d], {
size: 60,
fill: colors[d]
});
difficultyTitle.anchor.set(0.5, 0.5);
difficultyTitle.x = 350 + d * 350;
difficultyTitle.y = 600;
scoreboardScreen.addChild(difficultyTitle);
// Get scores for this difficulty
var scores = storage[storageKeys[d]] || [];
// Display top 5 scores
for (var s = 0; s < 5; s++) {
var scoreText = scores[s] ? scores[s].toString() : '-';
var scoreDisplay = new Text2(s + 1 + '. ' + scoreText, {
size: 40,
fill: 0xffffff
});
scoreDisplay.anchor.set(0.5, 0.5);
scoreDisplay.x = 350 + d * 350;
scoreDisplay.y = 700 + s * 60;
scoreboardScreen.addChild(scoreDisplay);
}
}
// Back button
var backText = new Text2('TAP TO CONTINUE', {
size: 60,
fill: 0x00ff88
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2200;
scoreboardScreen.addChild(backText);
// Store reference for cleanup
game.scoreboardScreen = scoreboardScreen;
}
function getHighestScore() {
var difficulties = ['easy', 'medium', 'hard'];
var highestScore = 0;
var highestDifficulty = 'none';
for (var d = 0; d < difficulties.length; d++) {
var storageKey = difficulties[d] + 'Scores';
var scores = storage[storageKey] || [];
if (scores.length > 0 && scores[0] > highestScore) {
highestScore = scores[0];
highestDifficulty = difficulties[d].toUpperCase();
}
}
return {
score: highestScore,
difficulty: highestDifficulty
};
}
function saveScore(score, difficulty) {
var storageKey = difficulty + 'Scores';
var scores = storage[storageKey] || [];
// Add new score
scores.push(score);
// Sort in descending order
scores.sort(function (a, b) {
return b - a;
});
// Keep only top 5
if (scores.length > 5) {
scores = scores.slice(0, 5);
}
// Save back to storage
storage[storageKey] = scores;
}
function createStartScreen() {
startScreen = new Container();
game.addChild(startScreen);
// Create animated start background
startBackground = startScreen.attachAsset('startBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.33
});
// Create multiple layers for depth effect
var bgLayer1 = LK.getAsset('startBg', {
anchorX: 0,
anchorY: 0,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.3
});
bgLayer1.x = -100;
bgLayer1.y = -100;
startScreen.addChild(bgLayer1);
var bgLayer2 = LK.getAsset('startBg', {
anchorX: 0,
anchorY: 0,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.5
});
bgLayer2.x = 200;
bgLayer2.y = 200;
startScreen.addChild(bgLayer2);
// Animate background layers
tween(bgLayer1, {
rotation: Math.PI * 2
}, {
duration: 20000,
repeat: -1
});
tween(bgLayer2, {
rotation: -Math.PI * 2
}, {
duration: 15000,
repeat: -1
});
// Title
var titleText = new Text2('RHYTHM SYNC', {
size: 120,
fill: 0xff00ff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
startScreen.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Beat Catcher', {
size: 60,
fill: 0x00ff88
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 920;
startScreen.addChild(subtitleText);
// Play button background
var playButtonBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
playButtonBg.x = 1024;
playButtonBg.y = 1200;
startScreen.addChild(playButtonBg);
// Play button text
var playText = new Text2('TAP TO PLAY', {
size: 80,
fill: 0xffff00
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1300;
startScreen.addChild(playText);
// Difficulty selector background
var selectorBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8.0,
scaleY: 1.5
});
selectorBg.x = 1024;
selectorBg.y = 1520;
selectorBg.alpha = 0.3;
startScreen.addChild(selectorBg);
// Difficulty button backgrounds
var easyBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2
});
easyBtnBg.x = 724;
easyBtnBg.y = 1520;
easyBtnBg.alpha = difficulty === 'easy' ? 0.8 : 0.3;
startScreen.addChild(easyBtnBg);
var mediumBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
tint: 0xffffff
});
mediumBtnBg.x = 1024;
mediumBtnBg.y = 1520;
mediumBtnBg.alpha = difficulty === 'medium' ? 0.8 : 0.3;
startScreen.addChild(mediumBtnBg);
var hardBtnBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2
});
hardBtnBg.x = 1324;
hardBtnBg.y = 1520;
hardBtnBg.alpha = difficulty === 'hard' ? 0.8 : 0.3;
startScreen.addChild(hardBtnBg);
// Difficulty buttons
var easyBtn = new Text2('EASY', {
size: 80,
fill: difficulty === 'easy' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
easyBtn.anchor.set(0.5, 0.5);
easyBtn.x = 724;
easyBtn.y = 1520;
startScreen.addChild(easyBtn);
var mediumBtn = new Text2('MEDIUM', {
size: 80,
fill: difficulty === 'medium' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
mediumBtn.anchor.set(0.5, 0.5);
mediumBtn.x = 1024;
mediumBtn.y = 1520;
startScreen.addChild(mediumBtn);
var hardBtn = new Text2('HARD', {
size: 80,
fill: difficulty === 'hard' ? 0x000000 : 0xffffff,
fontWeight: 'bold'
});
hardBtn.anchor.set(0.5, 0.5);
hardBtn.x = 1324;
hardBtn.y = 1520;
startScreen.addChild(hardBtn);
// Selection indicator
var selectionIndicator = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 1.0
});
selectionIndicator.alpha = 0.5;
selectionIndicator.x = difficulty === 'easy' ? 724 : difficulty === 'medium' ? 1024 : 1324;
selectionIndicator.y = 1520;
startScreen.addChild(selectionIndicator);
// Store buttons for easy access
startScreen.easyBtn = easyBtn;
startScreen.mediumBtn = mediumBtn;
startScreen.hardBtn = hardBtn;
startScreen.easyBtnBg = easyBtnBg;
startScreen.mediumBtnBg = mediumBtnBg;
startScreen.hardBtnBg = hardBtnBg;
startScreen.selectionIndicator = selectionIndicator;
startScreen.difficultySelected = true;
// Instructions
var instructText = new Text2('Drag the orb to collect notes on the beat!', {
size: 50,
fill: 0xff6600
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1600;
startScreen.addChild(instructText);
// Scoreboard button
var scoreboardBg = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 1.0
});
scoreboardBg.x = 1024;
scoreboardBg.y = 1750;
scoreboardBg.alpha = 0.4;
startScreen.addChild(scoreboardBg);
var scoreboardText = new Text2('HIGH SCORES', {
size: 60,
fill: 0x00ffff
});
scoreboardText.anchor.set(0.5, 0.5);
scoreboardText.x = 1024;
scoreboardText.y = 1750;
startScreen.addChild(scoreboardText);
// Add highest score display under scoreboard button
var highestData = getHighestScore();
var highestScoreText = '';
if (highestData.score > 0) {
highestScoreText = 'Best: ' + highestData.score + ' (' + highestData.difficulty + ')';
} else {
highestScoreText = 'No scores yet';
}
var bestScoreDisplay = new Text2(highestScoreText, {
size: 40,
fill: 0xffaa00
});
bestScoreDisplay.anchor.set(0.5, 0.5);
bestScoreDisplay.x = 1024;
bestScoreDisplay.y = 1800;
startScreen.addChild(bestScoreDisplay);
// Animate title
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
yoyo: true,
repeat: -1
});
// Play background music for start screen
LK.playMusic('bgmusic');
}
function startGame() {
gameState = 'playing';
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Create animated game background
gameBackground = game.attachAsset('gameBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.0,
scaleY: 1.54
});
// Create floating elements for atmosphere
var floatingElements = [];
for (var i = 0; i < 8; i++) {
var element = LK.getAsset('orb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
element.alpha = 0.1;
element.x = Math.random() * 2048;
element.y = Math.random() * 2732;
game.addChild(element);
floatingElements.push(element);
// Animate floating elements
tween(element, {
y: element.y + (Math.random() * 400 - 200),
x: element.x + (Math.random() * 400 - 200),
alpha: 0.3
}, {
duration: 3000 + Math.random() * 2000,
yoyo: true,
repeat: -1
});
}
// Create orb
orb = game.addChild(new Orb());
orb.x = 1024;
orb.y = 1366;
// Reset beat timing to sync with music start
gameStartTick = LK.ticks;
beatCounter = 0;
gameTimer = 0;
// Create timer display for game
timerTxt = new Text2('22', {
size: 80,
fill: 0xFFFF00
});
timerTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timerTxt);
timerTxt.x = -50;
timerTxt.y = 50;
// Start music based on difficulty (this will automatically stop the current bgmusic)
var musicTrack = difficulty === 'easy' ? 'easyMusic' : difficulty === 'medium' ? 'mediumMusic' : 'hardMusic';
LK.playMusic(musicTrack);
}
function spawnNote() {
var note = new Note();
note.x = Math.random() * 1848 + 100; // Keep notes within screen bounds
note.y = Math.random() * 2532 + 100;
notes.push(note);
game.addChild(note);
}
function updateBackground() {
var targetColor = neutralColor;
if (facekitAvailable) {
if (facekit.mouthOpen) {
targetColor = mouthOpenColor;
} else if (facekit.smile) {
targetColor = smileColor;
} else if (facekit.frown) {
targetColor = frownColor;
}
}
if (targetColor !== currentBgColor) {
currentBgColor = targetColor;
game.setBackgroundColor(targetColor);
}
}
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (gameState === 'start') {
// Check if clicked on difficulty buttons
if (startScreen && y >= 1480 && y <= 1560) {
if (x >= 650 && x <= 800) {
// Easy button clicked
difficulty = 'easy';
difficultySelected = true;
startScreen.easyBtn.fill = 0x000000;
startScreen.mediumBtn.fill = 0xffffff;
startScreen.hardBtn.fill = 0xffffff;
startScreen.easyBtnBg.alpha = 0.8;
startScreen.mediumBtnBg.alpha = 0.3;
startScreen.hardBtnBg.alpha = 0.3;
startScreen.selectionIndicator.x = 724;
} else if (x >= 950 && x <= 1100) {
// Medium button clicked
difficulty = 'medium';
difficultySelected = true;
startScreen.easyBtn.fill = 0xffffff;
startScreen.mediumBtn.fill = 0x000000;
startScreen.hardBtn.fill = 0xffffff;
startScreen.easyBtnBg.alpha = 0.3;
startScreen.mediumBtnBg.alpha = 0.8;
startScreen.hardBtnBg.alpha = 0.3;
startScreen.selectionIndicator.x = 1024;
} else if (x >= 1250 && x <= 1400) {
// Hard button clicked
difficulty = 'hard';
difficultySelected = true;
startScreen.easyBtn.fill = 0xffffff;
startScreen.mediumBtn.fill = 0xffffff;
startScreen.hardBtn.fill = 0x000000;
startScreen.easyBtnBg.alpha = 0.3;
startScreen.mediumBtnBg.alpha = 0.3;
startScreen.hardBtnBg.alpha = 0.8;
startScreen.selectionIndicator.x = 1324;
}
} else if (y >= 1700 && y <= 1800 && x >= 800 && x <= 1250) {
// Scoreboard button clicked
showScoreboard();
} else if (difficultySelected) {
// Start game only if difficulty is selected
startGame();
}
} else if (gameState === 'scoreboard') {
// Return to start screen from scoreboard
if (game.scoreboardScreen) {
game.scoreboardScreen.destroy();
game.scoreboardScreen = null;
}
gameState = 'start';
createStartScreen();
} else if (gameState === 'scoreboard') {
// Return to start screen from scoreboard
if (game.scoreboardScreen) {
game.scoreboardScreen.destroy();
game.scoreboardScreen = null;
}
gameState = 'start';
createStartScreen();
} else if (gameState === 'playing' && orb) {
dragNode = orb;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (gameState === 'playing') {
dragNode = null;
}
};
game.update = function () {
if (gameState === 'playing') {
// Beat timing based on ticks for stable synchronization
var beatInterval = difficulty === 'easy' ? 45 : difficulty === 'medium' ? 30 : 20;
var beatWindowDuration = difficulty === 'easy' ? 12 : difficulty === 'medium' ? 8 : 5;
var spawnChance = difficulty === 'easy' ? 0.5 : difficulty === 'medium' ? 0.7 : 0.9;
// Check if we've hit a new beat using ticks since game started
var ticksSinceStart = LK.ticks - gameStartTick;
if (ticksSinceStart % beatInterval === 0 && ticksSinceStart > 0) {
beatCounter++;
// Start beat window
beatWindow = true;
beatWindowCounter = 0;
// Pulse all notes
for (var i = 0; i < notes.length; i++) {
notes[i].pulse();
}
// Glow orb during beat window
if (orb) {
orb.startGlow();
}
// Spawn new note based on difficulty
if (Math.random() < spawnChance) {
spawnNote();
}
}
// Manage beat window duration based on difficulty
if (beatWindow) {
beatWindowCounter++;
if (beatWindowCounter >= beatWindowDuration) {
beatWindow = false;
if (orb) {
orb.stopGlow();
}
// Fade out notes after beat window
for (var i = 0; i < notes.length; i++) {
notes[i].fadeOut();
}
}
}
// Check for note collection
if (orb) {
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (!note.isCollected && orb.intersects(note)) {
if (beatWindow && note.isVisible) {
// Perfect timing - full points
LK.setScore(LK.getScore() + 10);
LK.getSound('pulse').play();
} else if (note.isVisible) {
// Good timing - half points
LK.setScore(LK.getScore() + 5);
LK.getSound('pulse').play();
}
note.isCollected = true;
note.destroy();
notes.splice(i, 1);
scoreTxt.setText(LK.getScore());
}
}
}
// Update timer display
if (timerTxt) {
var timeLeft = Math.ceil((gameTimerMax - gameTimer) / 60);
timerTxt.setText(timeLeft.toString());
}
// Clean up old notes that haven't been collected
if (LK.ticks % 180 === 0) {
// Every 3 seconds
for (var i = notes.length - 1; i >= 0; i--) {
if (Math.random() < 0.3) {
// 30% chance to remove old notes
notes[i].destroy();
notes.splice(i, 1);
}
}
}
// Update background based on facial expressions (if camera available)
if (facekitAvailable) {
updateBackground();
}
// Update game timer
gameTimer++;
// End game when timer reaches maximum
if (gameTimer >= gameTimerMax) {
// Save the score before showing game over
saveScore(LK.getScore(), difficulty);
LK.showGameOver();
}
// Win condition at 500 points
if (LK.getScore() >= 500) {
// Save the score before showing win screen
saveScore(LK.getScore(), difficulty);
LK.showYouWin();
}
}
};
// Initialize facekit for facial expression detection if camera is available
var facekitAvailable = false;
// Create start screen
createStartScreen();
A futuristic abstract 2D background for a rhythm game start screen. Neon color palette — cyan, magenta, and deep purple. Floating glowing spheres, waveforms, and pulse rings layered over a dark space-like gradient background. Visual elements should feel musical — like a living audio visualizer. High detail, soft glowing effects, minimal UI clutter space in center. Style: Digital art, vibrant, atmospheric.. In-Game asset. 2d. High contrast. No shadows
Design a seamless looping 2D abstract background for a rhythm-based game. Color palette: dark blue, black, soft purples — with faint neon pulses. Include soft gradients, subtle waveform lines, and floating particles or rings. The background should feel immersive and reactive, but not distract from gameplay elements. Style: futuristic, ambient, minimalistic digital art. No characters or sharp objects. Ideal for side-scrolling or freely moving 2D rhythm gameplay.. In-Game asset. 2d. High contrast. No shadows
a round shape futuristic speaker. In-Game asset. 2d. High contrast. No shadows