User prompt
update with: function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Normalize angle and apply dampening return Math.max(-30, Math.min(30, angle * 0.3)); } return 0; }
User prompt
update with: // Adjust these variables at the top of pufferMask var tiltSmoothingFactor = 0.05; // Reduced from 0.08 for smoother movement var tiltScaleFactor = 0.08; // Reduced from 0.15 for less tilt function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced max angle to ±15 degrees and lowered multiplier return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; }
Code edit (2 edits merged)
Please save this source code
User prompt
update as needed with: // Adjust the smoothing variables at the top of pufferMask var smoothingFactor = 0.12; // Slightly down from 0.15 var lastX = 0; var lastY = 0; var velocityX = 0; var velocityY = 0; var velocityDamping = 0.85; // Damping factor to reduce sudden movements // Then in the update function, modify the position tracking: self.update = function() { // Follow nose position with momentum if (facekit.noseTip) { targetX = facekit.noseTip.x; targetY = facekit.noseTip.y; // Calculate velocity with dampening velocityX = (velocityX * velocityDamping) + (targetX - lastX) * smoothingFactor; velocityY = (velocityY * velocityDamping) + (targetY - lastY) * smoothingFactor; // Update position using velocity self.x += velocityX; self.y += velocityY; // Store last position lastX = self.x; lastY = self.y; } // Rest of the update function remains the same...
User prompt
update as needed with: // In pufferMask, replace the position tracking variables with: var smoothingFactor = 0.12; var prevX = null; var prevY = null; // Then in the update function, modify the position tracking part: self.update = function() { // Follow nose position with weighted averaging if (facekit.noseTip) { targetX = facekit.noseTip.x; targetY = facekit.noseTip.y; // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } // Rest of the update function remains the same...
Code edit (1 edits merged)
Please save this source code
User prompt
adjust the scale of the puffermask based on detected face size
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: baseScale is not defined' in or related to this line: 'var newScale = baseScale + faceWidth * faceSizeScaleFactor;' Line Number: 104
User prompt
update with: var baseScale = 0.3; // Reduced base size var faceSizeScaleFactor = 0.003; // Increased sensitivity to face size
Code edit (8 edits merged)
Please save this source code
User prompt
update with: // Change these variables at the start of pufferMask var baseScale = 1; var minScale = 0.1; var maxScale = 3; // Replace the face size scaling section in update() with this: // Adjust scale based on face size if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Get eye distance var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); // Get face height (eye to mouth distance) var faceHeight = Math.abs(((facekit.leftEye.y + facekit.rightEye.y) / 2) - facekit.mouthCenter.y); // Use both measurements for better face size estimation var faceSize = (eyeDistance + faceHeight) / 2; // Calculate new scale with better normalization var newScale = (faceSize / 100); // Normalize to a base face size of 100 pixels // Clamp scale between min and max values newScale = Math.max(minScale, Math.min(maxScale, newScale)); // Smooth the scaling sprite.scaleX = sprite.scaleX * 0.8 + newScale * 0.2; sprite.scaleY = sprite.scaleY * 0.8 + newScale * 0.2; }
User prompt
update with: // In the pufferMask update function, replace the face size scaling section with: if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Get eye distance for width var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); // Get face height (eye to mouth distance) var faceHeight = Math.abs(((facekit.leftEye.y + facekit.rightEye.y) / 2) - facekit.mouthCenter.y); // Use eye distance directly for scaling var newScale = eyeDistance / 50; // Adjust this divisor to change base scale // Smooth the scaling but with more responsiveness sprite.scaleX = sprite.scaleX * 0.6 + newScale * 0.4; sprite.scaleY = sprite.scaleY * 0.6 + newScale * 0.4; }
User prompt
update with: if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); // Adjust divisor to account for larger base image var newScale = eyeDistance / 500; // Increased from 50 to 500 for 1500px base size sprite.scaleX = sprite.scaleX * 0.6 + newScale * 0.4; sprite.scaleY = sprite.scaleY * 0.6 + newScale * 0.4; }
User prompt
update with: // Add these variables at the start of pufferMask var scaleHistory = new Array(5).fill(0); // Keep last 5 scale values var scaleIndex = 0; // Replace the scaling section in update(): if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 500; // Update rolling average scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce((a, b) => a + b, 0) / scaleHistory.length; // More gentle smoothing sprite.scaleX = sprite.scaleX * 0.85 + avgScale * 0.15; sprite.scaleY = sprite.scaleY * 0.85 + avgScale * 0.15; }
Code edit (1 edits merged)
Please save this source code
User prompt
update as needed with: // Track current growing bubble game.growingBubble = null; game.maxBubbleSize = 100; // Base max size before upgrades game.growthRate = 1; // Size units per 0.1 seconds (10 per second) // Add to game.update: if (facekit.mouthCenter && facekit.mouthOpen) { // Start or continue growing bubble if (!game.growingBubble) { game.growingBubble = new Bubble(); game.growingBubble.size = 25; // Minimum starting size game.addChild(game.growingBubble); game.bubbles.push(game.growingBubble); } // Update growing bubble position and size if (game.growingBubble) { game.growingBubble.x = facekit.mouthCenter.x; game.growingBubble.y = facekit.mouthCenter.y; game.growingBubble.size = Math.min( game.growingBubble.size + game.growthRate, game.maxBubbleSize ); game.growingBubble.verticalVelocity = 0; // Stay in place while growing game.growingBubble.driftX = 0; } } else if (game.growingBubble) { // Release bubble when mouth closes game.growingBubble.verticalVelocity = -5; // Initial downward velocity game.growingBubble.driftX = (Math.random() * 2 - 1) * 2; // Small random horizontal drift game.growingBubble = null; } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
update as needed with: // Update these values at game initialization game.maxBubbleSize = 130; // Increased by 30% game.growthRate = 1.3; // Slightly increased to match new max size // Replace the mouth position check in game.update with: if (facekit.mouthOpen) { // Calculate spawn position relative to pufferfish mask var spawnX = playerMask.x; // Center of mask var spawnY = playerMask.y + (playerMask.height * 0.4); // 40% from bottom // Start or continue growing bubble if (!game.growingBubble) { game.growingBubble = new Bubble(); game.growingBubble.size = 25; // Keep minimum starting size game.addChild(game.growingBubble); game.bubbles.push(game.growingBubble); } // Update growing bubble position and size if (game.growingBubble) { game.growingBubble.x = spawnX; game.growingBubble.y = spawnY; game.growingBubble.size = Math.min( game.growingBubble.size + game.growthRate, game.maxBubbleSize ); game.growingBubble.verticalVelocity = 0; game.growingBubble.driftX = 0; } } else if (game.growingBubble) { // Stronger initial downward velocity when released game.growingBubble.verticalVelocity = -8; // Increased from -5 game.growingBubble.driftX = (Math.random() * 2 - 1) * 2.5; // Slightly increased drift game.growingBubble = null; } ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
Code edit (6 edits merged)
Please save this source code
User prompt
update as needed with: var Bubble = Container.expand(function() { // ... existing init code ... self.lifetime = 0; self.AUTO_POP_SIZE = 40; self.MIN_SPLIT_SIZE = 30; self.update = function() { self.lifetime++; // Auto-pop small bubbles after 3 seconds if(self.size <= self.AUTO_POP_SIZE && self.lifetime > 180) { self.autoPop(); return; } // Clear off-screen bubbles if(self.y < -self.size || self.y > game.height + self.size) { self.destroy(); return; } // ... existing update code ... }; self.autoPop = function() { var points = Math.floor(self.getBP() * 0.5); // Half points for auto-pop game.addBP(points); self.destroy(); }; });
User prompt
update bubble with: self.lifetime = 0; self.AUTO_POP_SIZE = 40; self.MIN_SPLIT_SIZE = 30;
User prompt
update with: if (self.size > 60 && !self.justSplit) { var newSize = Math.max(self.MIN_SPLIT_SIZE, self.size * 0.6); if(newSize >= self.MIN_SPLIT_SIZE) { for (var i = 0; i < 2; i++) { spawnSplitBubble(self.x, self.y, newSize, i === 0 ? -1 : 1); } } }
User prompt
update as needed with: // Near other game variables game.MAX_BUBBLES = 125; game.baseSpawnRate = 180; // Every 3 seconds // In game update game.update = function() { // Only spawn if under max bubbles if(game.bubbles.length < game.MAX_BUBBLES) { if (LK.ticks % game.baseSpawnRate == 0) { var x = Math.random() * (game.width - 200) + 100; spawnSplitBubble(x, game.height + 100, 100, 0); } } // Clean up destroyed bubbles from array game.bubbles = game.bubbles.filter(function(bubble) { return !bubble.destroyed; }); // ... rest of update code ... };
User prompt
update with: if (LK.ticks % game.baseSpawnRate == 0) { console.log('Attempting spawn, bubbles:', game.bubbles.length); var x = Math.random() * (game.width - 200) + 100; spawnSplitBubble(x, game.height + 100, 100, 0); }
User prompt
update with: // Change this: if(self.y < -self.size || self.y > game.height + self.size) { self.destroy(); return; } // To this: if(self.y < -self.size) { self.destroy(); return; }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Bubble class to represent each bubble in the game
var Bubble = Container.expand(function () {
var self = Container.call(this);
self.lastPopTime = 0; // Add timestamp tracking
var sprite = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.size = 100;
// Subtle size-based variance plus small random factor
var speedMultiplier = 120 / self.size * (0.9 + Math.random() * 0.2); // Just 10% variance
self.floatSpeed = 50 * speedMultiplier / 60;
self.driftX = (Math.random() * 20 - 10) / 60; // Normal drift variance
self.verticalVelocity = 0;
self.down = function (e) {
// Add cooldown check (100ms)
var currentTime = Date.now();
if (currentTime - self.lastPopTime < 100) {
return true; // Ignore clicks too close together
}
self.lastPopTime = currentTime;
var index = game.bubbles.indexOf(self);
if (index > -1) {
game.bubbles.splice(index, 1);
}
var points = self.getBP();
game.addBP(points);
if (self.size > 60 && !self.justSplit) {
var newSize = self.size * 0.6;
for (var i = 0; i < 2; i++) {
spawnSplitBubble(self.x, self.y, newSize, i === 0 ? -1 : 1);
}
}
self.destroy();
return true; // Stop event propagation
};
self.getBP = function () {
return Math.floor(Math.pow(self.size, 2) * 0.1);
};
self.update = function () {
self.justSplit = false; // Clear the flag after first update
// Add gravity effect
if (self.verticalVelocity < self.floatSpeed) {
self.verticalVelocity += 0.1; // Gradually transition to floating
}
self.y -= self.verticalVelocity;
// Gradually reduce horizontal speed after split
if (Math.abs(self.driftX) > (Math.random() * 20 - 10) / 60) {
self.driftX *= 0.98; // Slowly return to normal drift speed
}
self.x += self.driftX;
// Bounce off edges
if (self.x < self.size) {
self.x = self.size;
self.driftX = Math.abs(self.driftX);
} else if (self.x > game.width - self.size) {
self.x = game.width - self.size;
self.driftX = -Math.abs(self.driftX);
}
var scale = self.size / sprite.width;
sprite.scaleX = scale;
sprite.scaleY = scale;
};
return self;
});
// Pufferfish mask that follows face
var pufferMask = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('pufferfish', {
anchorX: 0.5,
anchorY: 0.5
});
var targetX = 0;
var targetY = 0;
var smoothingFactor = 0.15; // Adjust between 0-1 (lower = smoother)
var targetRotation = 0;
var rotationSmoothingFactor = 0.1;
var targetTilt = 0;
var tiltSmoothingFactor = 0.08; // Reduced from 0.12
var tiltScaleFactor = 0.15; // Reduced from 0.2
var mouthOpenCounter = 0;
var mouthOpenThreshold = 3; // Adjust frames needed to trigger state change
var isBlowing = false;
var baseScale = 1;
var blowScale = 0.8;
self.update = function () {
// Follow nose position for main face tracking
if (facekit.noseTip) {
targetX = facekit.noseTip.x;
targetY = facekit.noseTip.y;
self.x += (targetX - self.x) * smoothingFactor;
self.y += (targetY - self.y) * smoothingFactor;
}
if (facekit.leftEye && facekit.rightEye) {
targetTilt = calculateFaceTilt() * tiltScaleFactor; // Scale down the tilt
// Reduce max rotation to ±15 degrees
targetTilt = Math.max(-15, Math.min(15, targetTilt));
self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor;
}
// Track mouth state for blowing animation
if (facekit.mouthCenter) {
if (facekit.mouthOpen) {
mouthOpenCounter++;
if (mouthOpenCounter >= mouthOpenThreshold && !isBlowing) {
isBlowing = true;
tween(sprite, {
scaleX: blowScale,
scaleY: blowScale
}, {
duration: 15
});
}
} else {
mouthOpenCounter = 0;
if (isBlowing) {
isBlowing = false;
tween(sprite, {
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 10
});
}
}
}
};
function calculateFaceTilt() {
if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) {
// Calculate midpoint between eyes
var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2;
// Calculate angle between eye midpoint and mouth, negated to fix direction
var dx = facekit.mouthCenter.x - eyeMidX;
var dy = facekit.mouthCenter.y - eyeMidY;
var angle = -(Math.atan2(dx, dy) * (180 / Math.PI));
// Normalize angle and apply dampening
return Math.max(-30, Math.min(30, angle * 0.3));
}
return 0; // Default to straight when face points aren't available
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Light blue background to represent the sky
});
/****
* Game Code
****/
var playerMask = new pufferMask();
game.addChild(playerMask);
// Separate spawn functions:
function spawnSplitBubble(parentX, parentY, size, direction) {
var bubble = new Bubble();
bubble.x = parentX;
bubble.y = parentY;
bubble.size = size;
bubble.justSplit = true; // Add this flag
// More reasonable velocities
var speedMultiplier = 120 / size * (0.9 + Math.random() * 0.2);
bubble.verticalVelocity = -(Math.random() * 2 + 4); // More reasonable downward speed
bubble.driftX = direction * (Math.random() * 2 + 4); // More reasonable spread
bubble.floatSpeed = (45 + Math.random() * 10) / 60 * speedMultiplier;
game.addChild(bubble);
game.bubbles.push(bubble);
}
game.bubbles = []; // Track bubble array
// Initialize game variables
//<Assets used in the game will automatically appear here>
game.bp = 0; // Track total BP
function formatBP(value) {
var units = ['', 'K', 'M', 'B', 'T'];
var unitIndex = 0;
while (value >= 1000 && unitIndex < units.length - 1) {
value /= 1000;
unitIndex++;
}
return Math.floor(value * 10) / 10 + units[unitIndex];
}
// Create BP display text (add near game initialization)
var bpText = new Text2("BP: 0", {
size: 100,
fill: 0xFFFFFF
});
bpText.anchor.set(0.5, 0);
bpText.x = game.width - 200; // Position in top right
bpText.y = 100;
game.addChild(bpText);
game.addBP = function (points) {
game.bp += points;
// Update display
bpText.text = formatBP(game.bp) + " BP";
};
game.update = function () {
// Spawn new bubbles
if (LK.ticks % 120 == 0) {
var x = Math.random() * (game.width - 200) + 100;
spawnSplitBubble(x, game.height + 100, 100, 0);
}
// Update all children (bubbles)
for (var i = game.bubbles.length - 1; i >= 0; i--) {
var bubble = game.bubbles[i];
if (bubble.update) {
bubble.update();
}
}
};
// Handle touch/mouse events for the game
game.down = function (x, y, obj) {
var popped = false; // Track if we've popped any bubble
for (var i = game.bubbles.length - 1; i >= 0; i--) {
var bubble = game.bubbles[i];
// Calculate distance between click and bubble center
var dx = x - bubble.x;
var dy = y - bubble.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only pop if we haven't popped anything this click
if (!popped && distance <= bubble.size / 2 + 10 && bubble.down) {
bubble.down();
popped = true;
break; // Exit loop after first pop
}
}
};
;
A treasure chest with gold coins. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A golden skull with diamonds for eyes. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A golden necklace with a ruby pendant. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A filled in white circle.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A yellow star. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a game logo for a game called 'Bubble Blower Tycoon' about a happy purple pufferfish with yellow fins and spines that builds an underwater empire of bubbles. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
an SVG of the word 'Start'. word should be yellow and the font should look like its made out of bubbles. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
bubblelow
Sound effect
backgroundmusic
Music
bubblehigh
Sound effect
bubble1
Sound effect
bubble2
Sound effect
bubble3
Sound effect
bubble4
Sound effect
blowing
Sound effect
bubbleshoot
Sound effect
fishtank
Sound effect
menuopen
Sound effect
upgrade
Sound effect
jellyfish
Sound effect
titlemusic
Music
startbutton
Sound effect