User prompt
could we also add a booster block along with the others for the player to collect which will increase the players speed and increase the tempo of the music. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
make it so!
User prompt
make it so!
User prompt
sounds good make it so!
User prompt
sick! make it so please!
User prompt
can you move the 2 buttons a little higher
User prompt
make it so!
User prompt
cool! love this! make it so! βͺπ‘ Consider importing and using the following plugins: @upit/facekit.v1
User prompt
try again βͺπ‘ Consider importing and using the following plugins: @upit/facekit.v1
User prompt
For the background image can you Add a moving neon grid or subtle scrolling starfield for synthwave effect Use a linear gradient (top to bottom) in dark purples/blues βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
make it so!
User prompt
cool sounds good make it so !
Code edit (1 edits merged)
Please save this source code
User prompt
Sound Surfer
Initial prompt
Create a simple rhythm game called "Sound Surfer" with the following features: A main menu that will be within a black and neon box with two options: option 1 will include use of the facekit which will use the mic for recording the music and beats that the player decides to use. Option 2 will NOT use the facekit and mic for beats/music and instead will use a randomly generated beat/music that you will create for the player. The game has three vertical lanes: left, center, and right. The player is a small glowing cool looking ship like from F-zero games at the bottom of the screen that can switch between lanes by clicking. Clicking on the left half of the screen moves the player one lane to the left. Clicking on the right half of the screen moves the player one lane to the right. The player cannot move outside the 3 lanes. Colored blocks spawn at the top of the screen in a random lane and fall down at a constant speed. When a block reaches the playerβs lane and collides with the player, the block disappears, and: +10 points are added to the score. A combo counter increases. If the block passes the player without a collision, the combo resets. Display the current "score" and "combo" in the top corner of the screen. Use a minimal neon/synthwave visual style: Dark background Bright glowing blocks and player Simple grid or lines to separate the lanes Use simple logic for now: Spawn a block every 1 second using a timer.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var BackgroundGrid = Container.expand(function () {
var self = Container.call(this);
// Create horizontal grid lines
self.horizontalLines = [];
for (var i = 0; i < 15; i++) {
var line = self.attachAsset('gridLine', {
anchorX: 0,
anchorY: 0.5,
alpha: 0.3
});
line.y = i * 200 - 400;
self.horizontalLines.push(line);
}
// Create vertical grid lines
self.verticalLines = [];
for (var j = 0; j < 8; j++) {
var vLine = self.attachAsset('gridLineVertical', {
anchorX: 0.5,
anchorY: 0,
alpha: 0.2
});
vLine.x = j * 300;
self.verticalLines.push(vLine);
}
self.update = function () {
// Move horizontal lines down
for (var i = 0; i < self.horizontalLines.length; i++) {
var line = self.horizontalLines[i];
line.y += 2;
// Reset line position when it goes off screen
if (line.y > 2800) {
line.y = -100;
}
// Fade effect based on distance from center
var distanceFromCenter = Math.abs(line.y - 1366);
var maxDistance = 1366;
line.alpha = 0.3 * (1 - distanceFromCenter / maxDistance);
}
};
return self;
});
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGraphics = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.speed = 8;
self.lane = 0;
self.caught = false;
// Random neon colors
var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0066, 0x66ff00];
blockGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
self.y += self.speed;
};
return self;
});
var BoosterBlock = Container.expand(function () {
var self = Container.call(this);
var boosterGraphics = self.attachAsset('booster', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.speed = 8;
self.lane = 0;
self.caught = false;
// Distinctive golden/yellow color for booster
boosterGraphics.tint = 0xFFD700;
// Add pulsing animation to make it stand out
tween(boosterGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(boosterGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing animation
if (!self.caught) {
tween(boosterGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
};
return self;
});
var Ship = Container.expand(function (shipType) {
var self = Container.call(this);
// Use the provided shipType or default to classicShip
var assetType = shipType || 'classicShip';
var shipGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0
});
// Add glow effect
shipGraphics.filters = [];
self.currentLane = 1; // 0 = left, 1 = center, 2 = right
self.targetX = 1024; // center position
self.update = function () {
// Smooth movement to target lane
var diff = self.targetX - self.x;
if (Math.abs(diff) > 2) {
self.x += diff * 0.15;
} else {
self.x = self.targetX;
}
};
self.moveToLane = function (lane) {
self.currentLane = lane;
if (lane === 0) {
self.targetX = 341; // left lane
} else if (lane === 1) {
self.targetX = 1024; // center lane
} else if (lane === 2) {
self.targetX = 1707; // right lane
}
// Add movement animation
tween(shipGraphics, {
scaleX: 3.6,
scaleY: 2.4
}, {
duration: 100,
onFinish: function onFinish() {
tween(shipGraphics, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 100
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a0a
});
/****
* Game Code
****/
// Create synthwave background with gradient effect
game.setBackgroundColor(0x270050);
// Create moving background grid
var backgroundGrid = game.addChild(new BackgroundGrid());
// Array of synthwave tracks for classic mode
var synthwaveTracks = ['synthwave1', 'synthwave2', 'synthwave3'];
// Game state
var gameMode = 'menu'; // 'menu', 'classic', 'microphone'
var ship;
var blocks = [];
var score = 0;
var combo = 0;
var bestCombo = 0;
var lastSpawnTime = 0;
var spawnInterval = 1000; // 1 second
var gameSpeed = 1.0; // Speed multiplier for the game
var baseBlockSpeed = 8; // Base speed for blocks
var currentMusicTrack = null; // Track current playing music
// Beat detection variables
var audioBuffer = [];
var beatThreshold = 0.4;
var lastBeatTime = 0;
var minBeatInterval = 200; // Minimum ms between beats
var volumeHistory = [];
var bufferSize = 10;
var beatSensitivity = 1.5;
// Lane positions - equal width lanes
var lanePositions = [341, 1024, 1707];
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0x00FFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
var comboTxt = new Text2('Combo: 0', {
size: 50,
fill: 0xFF00FF
});
comboTxt.anchor.set(0, 0);
comboTxt.x = 50;
comboTxt.y = 120;
LK.gui.topLeft.addChild(comboTxt);
// Create mode selection buttons with neon glow outline
var classicButton = new Text2('1. CLASSIC MODE\nBuilt-in synthwave music', {
size: 60,
fill: 0x00FFFF,
stroke: 0x00FFFF,
strokeThickness: 3
});
classicButton.anchor.set(0.5, 0.5);
classicButton.x = 0;
classicButton.y = -300;
LK.gui.center.addChild(classicButton);
var micButton = new Text2('2. MICROPHONE MODE\nSync with your music', {
size: 60,
fill: 0xFF00FF,
stroke: 0xFF00FF,
strokeThickness: 3
});
micButton.anchor.set(0.5, 0.5);
micButton.x = 0;
micButton.y = -100;
LK.gui.center.addChild(micButton);
var instructionTxt = new Text2('TAP A BUTTON TO SELECT MODE', {
size: 50,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.y = 100;
LK.gui.center.addChild(instructionTxt);
// Create lane dividers
var lane1 = game.addChild(LK.getAsset('lane1', {
x: 682,
y: 0
}));
var lane2 = game.addChild(LK.getAsset('lane2', {
x: 1366,
y: 0
}));
// Create ship
ship = game.addChild(new Ship());
ship.x = 1024;
ship.y = 2400;
function startGame(mode) {
gameMode = mode;
score = 0;
combo = 0;
bestCombo = 0;
blocks = [];
lastSpawnTime = 0;
gameSpeed = 1.0; // Reset game speed
// Reset beat detection variables
audioBuffer = [];
volumeHistory = [];
lastBeatTime = 0;
// Hide menu elements
classicButton.visible = false;
micButton.visible = false;
instructionTxt.visible = false;
// Destroy existing ship and create new one with appropriate asset
if (ship) {
ship.destroy();
}
// Create ship with mode-specific asset
if (mode === 'classic') {
ship = game.addChild(new Ship('classicShip'));
} else {
ship = game.addChild(new Ship('microphoneShip'));
}
ship.x = 1024;
ship.y = 2400;
// Start background music only in classic mode
if (mode === 'classic') {
// Randomly select one of the three synthwave tracks
var randomTrack = synthwaveTracks[Math.floor(Math.random() * synthwaveTracks.length)];
currentMusicTrack = randomTrack;
LK.playMusic(randomTrack);
} else {
// Stop background music in microphone mode to focus on external audio
currentMusicTrack = null;
LK.stopMusic();
}
updateUI();
}
function spawnBlock() {
var block;
// 15% chance to spawn a booster block
if (Math.random() < 0.15) {
block = new BoosterBlock();
} else {
block = new Block();
}
var lane;
if (gameMode === 'microphone') {
// Create more musical patterns based on audio intensity
var intensity = facekit.volume;
if (intensity > 0.8) {
// High intensity - spawn in multiple lanes or center
lane = Math.random() > 0.5 ? 1 : Math.floor(Math.random() * 3);
} else if (intensity > 0.5) {
// Medium intensity - prefer outer lanes
lane = Math.random() > 0.5 ? 0 : 2;
} else {
// Low intensity - single random lane
lane = Math.floor(Math.random() * 3);
}
} else {
// Classic mode - random lane
lane = Math.floor(Math.random() * 3);
}
block.lane = lane;
block.x = lanePositions[lane];
block.y = -50;
block.speed = baseBlockSpeed * gameSpeed; // Apply game speed multiplier
blocks.push(block);
game.addChild(block);
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
comboTxt.setText('Combo: ' + combo + ' (Best: ' + bestCombo + ')');
}
function checkCollisions() {
for (var i = blocks.length - 1; i >= 0; i--) {
var block = blocks[i];
// Check if block is in ship's catch zone
if (!block.caught && block.y >= ship.y - 100 && block.y <= ship.y + 100) {
if (block.lane === ship.currentLane) {
// Block caught!
block.caught = true;
// Check if it's a booster block
if (block instanceof BoosterBlock) {
// Booster effects
score += 25 + combo * 3; // More points for booster
combo += 2; // Extra combo bonus
// Increase game speed (cap at 2.5x)
gameSpeed = Math.min(gameSpeed + 0.2, 2.5);
// Apply speed to all existing blocks
for (var j = 0; j < blocks.length; j++) {
blocks[j].speed = baseBlockSpeed * gameSpeed;
}
// Restart music with faster tempo (only in classic mode)
if (gameMode === 'classic' && currentMusicTrack) {
LK.stopMusic();
// Small delay before restarting music to avoid audio glitches
LK.setTimeout(function () {
LK.playMusic(currentMusicTrack);
}, 50);
}
// Special visual feedback for booster
LK.effects.flashScreen(0xFFD700, 300);
LK.effects.flashObject(ship, 0xFFD700, 500);
} else {
// Regular block
score += 10 + combo * 2;
combo++;
}
if (combo > bestCombo) {
bestCombo = combo;
}
// Visual feedback
LK.effects.flashObject(block, 0xffffff, 200);
tween(block, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200
});
LK.getSound('catch').play();
updateUI();
// Remove block
block.destroy();
blocks.splice(i, 1);
continue;
}
}
// Check if block missed (passed ship)
if (!block.caught && block.y > ship.y + 150) {
if (block.lane === ship.currentLane) {
// Block missed - reset combo
combo = 0;
LK.effects.flashScreen(0xff0000, 300);
LK.getSound('miss').play();
updateUI();
}
block.caught = true; // Mark as processed
}
// Remove blocks that are off screen
if (block.y > 2800) {
block.destroy();
blocks.splice(i, 1);
}
}
}
function detectBeat() {
var currentVolume = facekit.volume;
var currentTime = Date.now();
// Add current volume to history buffer
volumeHistory.push(currentVolume);
if (volumeHistory.length > bufferSize) {
volumeHistory.shift();
}
// Calculate average volume from buffer
var avgVolume = 0;
for (var i = 0; i < volumeHistory.length; i++) {
avgVolume += volumeHistory[i];
}
avgVolume = avgVolume / volumeHistory.length;
// Detect beat: current volume significantly higher than average
var volumeSpike = currentVolume > avgVolume * beatSensitivity;
var timeSinceLastBeat = currentTime - lastBeatTime;
var beatDetected = volumeSpike && currentVolume > beatThreshold && timeSinceLastBeat > minBeatInterval;
if (beatDetected) {
lastBeatTime = currentTime;
// Add visual feedback for beat detection
LK.effects.flashScreen(0x440088, 100);
return true;
}
return false;
}
function shouldSpawnBlock() {
if (gameMode === 'microphone') {
// Spawn based on beat detection from microphone input
return detectBeat();
} else {
// Classic mode - spawn every second
return LK.ticks - lastSpawnTime >= spawnInterval / (1000 / 60);
}
}
// Button event handlers
classicButton.down = function (x, y, obj) {
if (gameMode === 'menu') {
// Add button press animation
tween(classicButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(classicButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
startGame('classic');
}
};
micButton.down = function (x, y, obj) {
if (gameMode === 'menu') {
// Add button press animation
tween(micButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(micButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
startGame('microphone');
}
};
// Touch controls for lane switching during gameplay
game.down = function (x, y, obj) {
if (gameMode === 'menu') {
return;
}
// Lane switching
if (x < 1024) {
// Left side tapped - move left
if (ship.currentLane > 0) {
ship.moveToLane(ship.currentLane - 1);
}
} else {
// Right side tapped - move right
if (ship.currentLane < 2) {
ship.moveToLane(ship.currentLane + 1);
}
}
};
// Main game update
game.update = function () {
if (gameMode === 'menu') {
return;
}
// Spawn blocks
if (shouldSpawnBlock()) {
spawnBlock();
lastSpawnTime = LK.ticks;
}
// Check collisions
checkCollisions();
// Game over condition (optional - can be removed for endless play)
if (score >= 500) {
LK.showYouWin();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -67,8 +67,55 @@
self.y += self.speed;
};
return self;
});
+var BoosterBlock = Container.expand(function () {
+ var self = Container.call(this);
+ var boosterGraphics = self.attachAsset('booster', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 0.8
+ });
+ self.speed = 8;
+ self.lane = 0;
+ self.caught = false;
+ // Distinctive golden/yellow color for booster
+ boosterGraphics.tint = 0xFFD700;
+ // Add pulsing animation to make it stand out
+ tween(boosterGraphics, {
+ scaleX: 1.0,
+ scaleY: 1.0
+ }, {
+ duration: 500,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(boosterGraphics, {
+ scaleX: 0.8,
+ scaleY: 0.8
+ }, {
+ duration: 500,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Restart the pulsing animation
+ if (!self.caught) {
+ tween(boosterGraphics, {
+ scaleX: 1.0,
+ scaleY: 1.0
+ }, {
+ duration: 500,
+ easing: tween.easeInOut
+ });
+ }
+ }
+ });
+ }
+ });
+ self.update = function () {
+ self.y += self.speed;
+ };
+ return self;
+});
var Ship = Container.expand(function (shipType) {
var self = Container.call(this);
// Use the provided shipType or default to classicShip
var assetType = shipType || 'classicShip';
@@ -143,8 +190,11 @@
var combo = 0;
var bestCombo = 0;
var lastSpawnTime = 0;
var spawnInterval = 1000; // 1 second
+var gameSpeed = 1.0; // Speed multiplier for the game
+var baseBlockSpeed = 8; // Base speed for blocks
+var currentMusicTrack = null; // Track current playing music
// Beat detection variables
var audioBuffer = [];
var beatThreshold = 0.4;
var lastBeatTime = 0;
@@ -218,8 +268,9 @@
combo = 0;
bestCombo = 0;
blocks = [];
lastSpawnTime = 0;
+ gameSpeed = 1.0; // Reset game speed
// Reset beat detection variables
audioBuffer = [];
volumeHistory = [];
lastBeatTime = 0;
@@ -242,17 +293,25 @@
// Start background music only in classic mode
if (mode === 'classic') {
// Randomly select one of the three synthwave tracks
var randomTrack = synthwaveTracks[Math.floor(Math.random() * synthwaveTracks.length)];
+ currentMusicTrack = randomTrack;
LK.playMusic(randomTrack);
} else {
// Stop background music in microphone mode to focus on external audio
+ currentMusicTrack = null;
LK.stopMusic();
}
updateUI();
}
function spawnBlock() {
- var block = new Block();
+ var block;
+ // 15% chance to spawn a booster block
+ if (Math.random() < 0.15) {
+ block = new BoosterBlock();
+ } else {
+ block = new Block();
+ }
var lane;
if (gameMode === 'microphone') {
// Create more musical patterns based on audio intensity
var intensity = facekit.volume;
@@ -272,8 +331,9 @@
}
block.lane = lane;
block.x = lanePositions[lane];
block.y = -50;
+ block.speed = baseBlockSpeed * gameSpeed; // Apply game speed multiplier
blocks.push(block);
game.addChild(block);
}
function updateUI() {
@@ -287,10 +347,35 @@
if (!block.caught && block.y >= ship.y - 100 && block.y <= ship.y + 100) {
if (block.lane === ship.currentLane) {
// Block caught!
block.caught = true;
- score += 10 + combo * 2;
- combo++;
+ // Check if it's a booster block
+ if (block instanceof BoosterBlock) {
+ // Booster effects
+ score += 25 + combo * 3; // More points for booster
+ combo += 2; // Extra combo bonus
+ // Increase game speed (cap at 2.5x)
+ gameSpeed = Math.min(gameSpeed + 0.2, 2.5);
+ // Apply speed to all existing blocks
+ for (var j = 0; j < blocks.length; j++) {
+ blocks[j].speed = baseBlockSpeed * gameSpeed;
+ }
+ // Restart music with faster tempo (only in classic mode)
+ if (gameMode === 'classic' && currentMusicTrack) {
+ LK.stopMusic();
+ // Small delay before restarting music to avoid audio glitches
+ LK.setTimeout(function () {
+ LK.playMusic(currentMusicTrack);
+ }, 50);
+ }
+ // Special visual feedback for booster
+ LK.effects.flashScreen(0xFFD700, 300);
+ LK.effects.flashObject(ship, 0xFFD700, 500);
+ } else {
+ // Regular block
+ score += 10 + combo * 2;
+ combo++;
+ }
if (combo > bestCombo) {
bestCombo = combo;
}
// Visual feedback
synthwave neon glow audiosurf or f-zero like ship. In-Game asset. 2d. High contrast. No shadows
faint glowing outlines with a shimmer effect retro synthwave style. In-Game asset. 2d. High contrast. No shadows
a musical note thats bright and neon thats also really cool looking. In-Game asset. 2d. High contrast. No shadows
synthwave bright neon glow audiosurf or f-zero like ship In-Game asset. 2d. High contrast. No shadows
synthwave bright neon glow audiosurf or f-zero like ship In-Game asset. 2d. High contrast. No shadows. facing upright vertical
a musical note thats bright and neon thats also really cool looking. In-Game asset. 2d. High contrast. No shadows
- Shape: a glowing neon **circle or hexagon** - Color: bright **red or magenta** - Inner symbol: a small white or yellow **skull**, **explosion**, or **β οΈ warning icon** in the center - Glow: apply a soft outer glow that pulses slightly - Visual style: match the **synthwave aesthetic** (think neon, arcade, retro-futuristic) - Size: same as the existing note blocks - Animation: gently pulsate or shimmer while falling. In-Game asset. 2d. High contrast. No shadows
a bright laser vertical line retro style. In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows
a line that is set to the theme of this game that looks like lightsabre. In-Game asset. 2d. High contrast. No shadows
synthwave bright neon glow audiosurf or f-zero like ship In-Game asset. 2d. High contrast. No shadows. facing upright vertical 3d like