/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ColorTrail = Container.expand(function () {
var self = Container.call(this);
var points = [];
var maxPoints = 7;
var trailShapes = [];
self.addPoint = function (x, y) {
// Add a new point to the trail
points.unshift({
x: x,
y: y
});
// Create a visual for this point
var shape = self.shapeType === 'floatingSquare' ? 'floatingSquare' : 'floatingCircle';
var trailShape = LK.getAsset(shape, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
tint: 0xFFFFFF * Math.random(),
scaleX: 0.3,
scaleY: 0.3
});
trailShape.x = x;
trailShape.y = y;
self.addChild(trailShape);
trailShapes.unshift(trailShape);
// Animate the new point
tween(trailShape, {
alpha: 0
}, {
duration: 66,
easing: tween.linear,
onFinish: function onFinish() {
if (self.children.includes(trailShape)) {
self.removeChild(trailShape);
}
}
});
// Remove oldest points if we exceed maxPoints
if (points.length > maxPoints) {
points.pop();
}
// Remove oldest trail shapes if we exceed the maximum allowed
var maxTrailShapes = self.shapeType === 'floatingCircle' ? 100 : 14;
if (trailShapes.length > maxTrailShapes) {
var oldestShape = trailShapes.pop();
if (oldestShape && self.children.includes(oldestShape)) {
self.removeChild(oldestShape);
}
}
};
return self;
});
var FloatingShape = Container.expand(function (shapeType, color) {
var self = Container.call(this);
self.shapeType = shapeType || 'floatingCircle';
self.color = color || 0xFFFFFF;
// Create and attach the shape asset
var shape = self.attachAsset(self.shapeType, {
anchorX: 0.5,
anchorY: 0.5,
tint: self.color,
alpha: 0.7
});
// Initial properties
self.vx = Math.random() * 2 - 1; // Random x velocity
self.vy = Math.random() * 2 - 1; // Random y velocity
self.rotationSpeed = Math.random() * 0.02 - 0.01; // Random rotation speed
self.scalePulse = 0;
self.scalePulseSpeed = Math.random() * 0.03 + 0.01;
// Update method for floating behavior
self.update = function () {
// Calculate distance from cursor
var dx = self.x - lastX;
var dy = self.y - lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Move away from cursor if within a certain range
if (distance < 300) {
var moveAwaySpeed = 0.5; // Slow speed
self.vx += dx / distance * moveAwaySpeed;
self.vy += dy / distance * moveAwaySpeed;
}
// Update position based on velocity
self.x += self.vx;
self.y += self.vy;
// Add trail effect
if (self.shapeType === 'floatingCircle' && (Math.abs(self.x - self.lastX) > 5 || Math.abs(self.y - self.lastY) > 5)) {
var trailShape = LK.getAsset(self.shapeType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
tint: self.color,
scaleX: 0.5,
scaleY: 0.5
});
trailShape.x = self.x;
trailShape.y = self.y;
game.addChild(trailShape);
// Fade out and remove trail shape
tween(trailShape, {
alpha: 0
}, {
duration: 1000,
easing: tween.linear,
onFinish: function onFinish() {
if (game.children.includes(trailShape)) {
game.removeChild(trailShape);
}
}
});
}
// Update last known positions
self.lastX = self.x;
self.lastY = self.y;
// Removed the script that prevents perfect x and y traveling
// Check for collisions with other shapes
shapes.forEach(function (otherShape) {
if (otherShape !== self && self.intersects(otherShape)) {
// Calculate collision response
var dx = self.x - otherShape.x;
var dy = self.y - otherShape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = self.width / 2 + otherShape.width / 2 - distance;
if (overlap > 0) {
// Move shapes apart
var moveX = dx / distance * overlap / 2;
var moveY = dy / distance * overlap / 2;
self.x += moveX;
self.y += moveY;
otherShape.x -= moveX;
otherShape.y -= moveY;
// Reverse direction and increase speed
self.vx = -self.vx * 2;
self.vy = -self.vy * 2;
// Slow down to default speed over time
tween(self, {
vx: self.vx / 2,
vy: self.vy / 2
}, {
duration: 1000,
easing: tween.easeOut
});
// Random chance for collision to not work and cause spin
if (Math.random() > 0.5) {
self.rotationSpeed += 0.05;
}
}
}
});
// Apply rotation based on direction only if touching something
if (shapes.some(function (otherShape) {
return otherShape !== self && self.intersects(otherShape);
})) {
var angle = Math.atan2(self.vy, self.vx);
self.rotation = angle;
}
// Pulsing scale effect
self.scalePulse += self.scalePulseSpeed;
var scaleFactor = 1 + Math.sin(self.scalePulse) * 0.1;
self.scale.set(scaleFactor, scaleFactor);
// Bounce off edges
if (self.x < 0 || self.x > 2048) {
self.vx *= -1;
}
if (self.y < 0 || self.y > 2732) {
self.vy *= -1;
}
};
// Interactive events
self.down = function (x, y, obj) {
// Expand on touch
tween(self.scale, {
x: 1.5,
y: 1.5
}, {
duration: 300,
easing: tween.easeOutElastic
});
// Play a random sound
var sounds = ['soft_ping', 'gentle_chime', 'water_drop'];
var randomSound = sounds[Math.floor(Math.random() * sounds.length)];
LK.getSound(randomSound).play();
// Change color
var newColor = 0xFFFFFF * Math.random();
tween(shape, {
tint: newColor
}, {
duration: 500,
easing: tween.easeInOut
});
};
self.up = function (x, y, obj) {
// Return to original size, but with a different easing for visual interest
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 500,
easing: tween.easeOutBounce
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111133
});
/****
* Game Code
****/
// Custom function to monitor frame rate
var lastFrameTime = Date.now();
var frameRate = 60;
function getFrameRate() {
var now = Date.now();
var delta = now - lastFrameTime;
lastFrameTime = now;
frameRate = 1000 / delta;
return frameRate;
}
// Track if we're currently touching/dragging
var isDragging = false;
var lastX = 0;
var lastY = 0;
// Set up our floating shapes
var shapes = [];
var numShapes = 5; // Starting number of shapes
// Create color trail
var colorTrail = new ColorTrail();
game.addChild(colorTrail);
// Create welcome message
var welcomeText = new Text2("Touch anywhere to interact with shapes\nDrag to create patterns", {
size: 60,
fill: 0xFFFFFF
});
welcomeText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(welcomeText);
// Start the ambient music
LK.playMusic('ambient_melody', {
fade: {
start: 0,
end: 0.6,
duration: 3000
}
});
// Hide welcome message after a delay
LK.setTimeout(function () {
tween(welcomeText, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.gui.center.removeChild(welcomeText);
}
});
}, 5000);
// Initialize with some floating shapes
function createInitialShapes() {
for (var i = 0; i < numShapes; i++) {
var shapeType = Math.random() > 0.5 ? 'floatingCircle' : 'floatingSquare';
var shape = new FloatingShape(shapeType, 0xFFFFFF * Math.random());
// Random position within the game area
shape.x = Math.random() * 2048;
shape.y = Math.random() * 2732;
// Random initial scale
var initialScale = 0.5 + Math.random() * 1.5;
shape.scale.set(initialScale, initialScale);
game.addChild(shape);
shapes.push(shape);
}
}
// Create a new shape at a specific position
function createShapeAtPosition(x, y) {
var shapeType = Math.random() > 0.5 ? 'floatingCircle' : 'floatingSquare';
var newShape = new FloatingShape(shapeType, 0xFFFFFF * Math.random());
newShape.x = x;
newShape.y = y;
// Start with small scale and tween to full size
newShape.scale.set(1, 1);
game.addChild(newShape);
shapes.push(newShape);
// Play a gentle sound
LK.getSound('gentle_chime').play();
// Limit the maximum number of shapes to prevent performance issues
if (shapes.length > 15) {
var oldestShape = shapes.shift();
// Fade out the oldest shape
tween(oldestShape, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (oldestShape !== undefined && game.children.includes(oldestShape)) {
game.removeChild(oldestShape);
}
}
});
}
}
// Touch/drag handlers
game.down = function (x, y, obj) {
isDragging = true;
lastX = x;
lastY = y;
// Create a new shape occasionally on touch
if (Math.random() > 0.7) {
createShapeAtPosition(x, y);
}
};
game.move = function (x, y, obj) {
if (isDragging) {
// Add to the color trail on significant movement
if (Math.abs(x - lastX) > 10 || Math.abs(y - lastY) > 10) {
colorTrail.addPoint(x, y);
// Sometimes create a new shape as we drag
if (colorTrail.shapeType === 'floatingCircle' && Math.random() > 0.95) {
createShapeAtPosition(x, y);
}
}
lastX = x;
lastY = y;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Create a pulsing background effect
var backgroundPulse = LK.getAsset('floatingCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 15,
scaleY: 15,
alpha: 0.1,
tint: 0x336699
});
game.addChild(backgroundPulse);
// Function to pulse the background
function pulseBackground() {
var targetScale = 15 + Math.random() * 5;
var targetColor = 0xFFFFFF * Math.random();
tween(backgroundPulse, {
tint: targetColor
}, {
duration: 5000,
easing: tween.linear
});
tween(backgroundPulse.scale, {
x: targetScale,
y: targetScale
}, {
duration: 5000,
easing: tween.easeInOut,
onFinish: pulseBackground
});
}
// Occasionally add new shapes over time
function addRandomShapeOverTime() {
// Add a shape at a random position
var x = Math.random() * 2048;
var y = Math.random() * 2732;
createShapeAtPosition(x, y);
// Schedule the next shape addition
var nextTime = 10000 + Math.random() * 10000;
LK.setTimeout(addRandomShapeOverTime, nextTime);
}
// Periodically create gentle screen-wide color shifts
function createColorShift() {
var newColor = 0x000000 + Math.floor(Math.random() * 0x222222);
tween(game, {
backgroundColor: newColor
}, {
duration: 8000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.setTimeout(createColorShift, 20000 + Math.random() * 10000);
}
});
}
// Initialize the game
createInitialShapes();
pulseBackground();
LK.setTimeout(addRandomShapeOverTime, 5000);
LK.setTimeout(createColorShift, 10000);
// Main update loop
game.update = function () {
// All shape-specific updates are handled in their own update methods
// Make the background pulse gently
backgroundPulse.rotation += 0.0005;
// Function to detect if the device is mobile
function isMobileDevice() {
return typeof navigator !== 'undefined' && navigator.userAgent && /Mobi|Android/i.test(navigator.userAgent);
}
// Monitor frame rate and reduce shape count if lag is detected
if (getFrameRate() < 50 || isMobileDevice()) {
// Remove excess shapes to improve performance
while (shapes.length > 10) {
var shapeToRemove = shapes.pop();
if (shapeToRemove && game.children.includes(shapeToRemove)) {
game.removeChild(shapeToRemove);
}
}
// Optionally, reduce the number of new shapes being created
numShapes = Math.max(1, numShapes - 1);
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ColorTrail = Container.expand(function () {
var self = Container.call(this);
var points = [];
var maxPoints = 7;
var trailShapes = [];
self.addPoint = function (x, y) {
// Add a new point to the trail
points.unshift({
x: x,
y: y
});
// Create a visual for this point
var shape = self.shapeType === 'floatingSquare' ? 'floatingSquare' : 'floatingCircle';
var trailShape = LK.getAsset(shape, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
tint: 0xFFFFFF * Math.random(),
scaleX: 0.3,
scaleY: 0.3
});
trailShape.x = x;
trailShape.y = y;
self.addChild(trailShape);
trailShapes.unshift(trailShape);
// Animate the new point
tween(trailShape, {
alpha: 0
}, {
duration: 66,
easing: tween.linear,
onFinish: function onFinish() {
if (self.children.includes(trailShape)) {
self.removeChild(trailShape);
}
}
});
// Remove oldest points if we exceed maxPoints
if (points.length > maxPoints) {
points.pop();
}
// Remove oldest trail shapes if we exceed the maximum allowed
var maxTrailShapes = self.shapeType === 'floatingCircle' ? 100 : 14;
if (trailShapes.length > maxTrailShapes) {
var oldestShape = trailShapes.pop();
if (oldestShape && self.children.includes(oldestShape)) {
self.removeChild(oldestShape);
}
}
};
return self;
});
var FloatingShape = Container.expand(function (shapeType, color) {
var self = Container.call(this);
self.shapeType = shapeType || 'floatingCircle';
self.color = color || 0xFFFFFF;
// Create and attach the shape asset
var shape = self.attachAsset(self.shapeType, {
anchorX: 0.5,
anchorY: 0.5,
tint: self.color,
alpha: 0.7
});
// Initial properties
self.vx = Math.random() * 2 - 1; // Random x velocity
self.vy = Math.random() * 2 - 1; // Random y velocity
self.rotationSpeed = Math.random() * 0.02 - 0.01; // Random rotation speed
self.scalePulse = 0;
self.scalePulseSpeed = Math.random() * 0.03 + 0.01;
// Update method for floating behavior
self.update = function () {
// Calculate distance from cursor
var dx = self.x - lastX;
var dy = self.y - lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Move away from cursor if within a certain range
if (distance < 300) {
var moveAwaySpeed = 0.5; // Slow speed
self.vx += dx / distance * moveAwaySpeed;
self.vy += dy / distance * moveAwaySpeed;
}
// Update position based on velocity
self.x += self.vx;
self.y += self.vy;
// Add trail effect
if (self.shapeType === 'floatingCircle' && (Math.abs(self.x - self.lastX) > 5 || Math.abs(self.y - self.lastY) > 5)) {
var trailShape = LK.getAsset(self.shapeType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
tint: self.color,
scaleX: 0.5,
scaleY: 0.5
});
trailShape.x = self.x;
trailShape.y = self.y;
game.addChild(trailShape);
// Fade out and remove trail shape
tween(trailShape, {
alpha: 0
}, {
duration: 1000,
easing: tween.linear,
onFinish: function onFinish() {
if (game.children.includes(trailShape)) {
game.removeChild(trailShape);
}
}
});
}
// Update last known positions
self.lastX = self.x;
self.lastY = self.y;
// Removed the script that prevents perfect x and y traveling
// Check for collisions with other shapes
shapes.forEach(function (otherShape) {
if (otherShape !== self && self.intersects(otherShape)) {
// Calculate collision response
var dx = self.x - otherShape.x;
var dy = self.y - otherShape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = self.width / 2 + otherShape.width / 2 - distance;
if (overlap > 0) {
// Move shapes apart
var moveX = dx / distance * overlap / 2;
var moveY = dy / distance * overlap / 2;
self.x += moveX;
self.y += moveY;
otherShape.x -= moveX;
otherShape.y -= moveY;
// Reverse direction and increase speed
self.vx = -self.vx * 2;
self.vy = -self.vy * 2;
// Slow down to default speed over time
tween(self, {
vx: self.vx / 2,
vy: self.vy / 2
}, {
duration: 1000,
easing: tween.easeOut
});
// Random chance for collision to not work and cause spin
if (Math.random() > 0.5) {
self.rotationSpeed += 0.05;
}
}
}
});
// Apply rotation based on direction only if touching something
if (shapes.some(function (otherShape) {
return otherShape !== self && self.intersects(otherShape);
})) {
var angle = Math.atan2(self.vy, self.vx);
self.rotation = angle;
}
// Pulsing scale effect
self.scalePulse += self.scalePulseSpeed;
var scaleFactor = 1 + Math.sin(self.scalePulse) * 0.1;
self.scale.set(scaleFactor, scaleFactor);
// Bounce off edges
if (self.x < 0 || self.x > 2048) {
self.vx *= -1;
}
if (self.y < 0 || self.y > 2732) {
self.vy *= -1;
}
};
// Interactive events
self.down = function (x, y, obj) {
// Expand on touch
tween(self.scale, {
x: 1.5,
y: 1.5
}, {
duration: 300,
easing: tween.easeOutElastic
});
// Play a random sound
var sounds = ['soft_ping', 'gentle_chime', 'water_drop'];
var randomSound = sounds[Math.floor(Math.random() * sounds.length)];
LK.getSound(randomSound).play();
// Change color
var newColor = 0xFFFFFF * Math.random();
tween(shape, {
tint: newColor
}, {
duration: 500,
easing: tween.easeInOut
});
};
self.up = function (x, y, obj) {
// Return to original size, but with a different easing for visual interest
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 500,
easing: tween.easeOutBounce
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111133
});
/****
* Game Code
****/
// Custom function to monitor frame rate
var lastFrameTime = Date.now();
var frameRate = 60;
function getFrameRate() {
var now = Date.now();
var delta = now - lastFrameTime;
lastFrameTime = now;
frameRate = 1000 / delta;
return frameRate;
}
// Track if we're currently touching/dragging
var isDragging = false;
var lastX = 0;
var lastY = 0;
// Set up our floating shapes
var shapes = [];
var numShapes = 5; // Starting number of shapes
// Create color trail
var colorTrail = new ColorTrail();
game.addChild(colorTrail);
// Create welcome message
var welcomeText = new Text2("Touch anywhere to interact with shapes\nDrag to create patterns", {
size: 60,
fill: 0xFFFFFF
});
welcomeText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(welcomeText);
// Start the ambient music
LK.playMusic('ambient_melody', {
fade: {
start: 0,
end: 0.6,
duration: 3000
}
});
// Hide welcome message after a delay
LK.setTimeout(function () {
tween(welcomeText, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.gui.center.removeChild(welcomeText);
}
});
}, 5000);
// Initialize with some floating shapes
function createInitialShapes() {
for (var i = 0; i < numShapes; i++) {
var shapeType = Math.random() > 0.5 ? 'floatingCircle' : 'floatingSquare';
var shape = new FloatingShape(shapeType, 0xFFFFFF * Math.random());
// Random position within the game area
shape.x = Math.random() * 2048;
shape.y = Math.random() * 2732;
// Random initial scale
var initialScale = 0.5 + Math.random() * 1.5;
shape.scale.set(initialScale, initialScale);
game.addChild(shape);
shapes.push(shape);
}
}
// Create a new shape at a specific position
function createShapeAtPosition(x, y) {
var shapeType = Math.random() > 0.5 ? 'floatingCircle' : 'floatingSquare';
var newShape = new FloatingShape(shapeType, 0xFFFFFF * Math.random());
newShape.x = x;
newShape.y = y;
// Start with small scale and tween to full size
newShape.scale.set(1, 1);
game.addChild(newShape);
shapes.push(newShape);
// Play a gentle sound
LK.getSound('gentle_chime').play();
// Limit the maximum number of shapes to prevent performance issues
if (shapes.length > 15) {
var oldestShape = shapes.shift();
// Fade out the oldest shape
tween(oldestShape, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (oldestShape !== undefined && game.children.includes(oldestShape)) {
game.removeChild(oldestShape);
}
}
});
}
}
// Touch/drag handlers
game.down = function (x, y, obj) {
isDragging = true;
lastX = x;
lastY = y;
// Create a new shape occasionally on touch
if (Math.random() > 0.7) {
createShapeAtPosition(x, y);
}
};
game.move = function (x, y, obj) {
if (isDragging) {
// Add to the color trail on significant movement
if (Math.abs(x - lastX) > 10 || Math.abs(y - lastY) > 10) {
colorTrail.addPoint(x, y);
// Sometimes create a new shape as we drag
if (colorTrail.shapeType === 'floatingCircle' && Math.random() > 0.95) {
createShapeAtPosition(x, y);
}
}
lastX = x;
lastY = y;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Create a pulsing background effect
var backgroundPulse = LK.getAsset('floatingCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 15,
scaleY: 15,
alpha: 0.1,
tint: 0x336699
});
game.addChild(backgroundPulse);
// Function to pulse the background
function pulseBackground() {
var targetScale = 15 + Math.random() * 5;
var targetColor = 0xFFFFFF * Math.random();
tween(backgroundPulse, {
tint: targetColor
}, {
duration: 5000,
easing: tween.linear
});
tween(backgroundPulse.scale, {
x: targetScale,
y: targetScale
}, {
duration: 5000,
easing: tween.easeInOut,
onFinish: pulseBackground
});
}
// Occasionally add new shapes over time
function addRandomShapeOverTime() {
// Add a shape at a random position
var x = Math.random() * 2048;
var y = Math.random() * 2732;
createShapeAtPosition(x, y);
// Schedule the next shape addition
var nextTime = 10000 + Math.random() * 10000;
LK.setTimeout(addRandomShapeOverTime, nextTime);
}
// Periodically create gentle screen-wide color shifts
function createColorShift() {
var newColor = 0x000000 + Math.floor(Math.random() * 0x222222);
tween(game, {
backgroundColor: newColor
}, {
duration: 8000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.setTimeout(createColorShift, 20000 + Math.random() * 10000);
}
});
}
// Initialize the game
createInitialShapes();
pulseBackground();
LK.setTimeout(addRandomShapeOverTime, 5000);
LK.setTimeout(createColorShift, 10000);
// Main update loop
game.update = function () {
// All shape-specific updates are handled in their own update methods
// Make the background pulse gently
backgroundPulse.rotation += 0.0005;
// Function to detect if the device is mobile
function isMobileDevice() {
return typeof navigator !== 'undefined' && navigator.userAgent && /Mobi|Android/i.test(navigator.userAgent);
}
// Monitor frame rate and reduce shape count if lag is detected
if (getFrameRate() < 50 || isMobileDevice()) {
// Remove excess shapes to improve performance
while (shapes.length > 10) {
var shapeToRemove = shapes.pop();
if (shapeToRemove && game.children.includes(shapeToRemove)) {
game.removeChild(shapeToRemove);
}
}
// Optionally, reduce the number of new shapes being created
numShapes = Math.max(1, numShapes - 1);
}
};