User prompt
Play trackA music while A side playbutton is active. Stop it when it repressed
User prompt
NOOO IDIOT! DO NOT PLAY THE TRACKA MUSIC UNTIL PLAYER PRESSING THE PLAYBUTTON ON A SIDE!
User prompt
Play trackA music if player pressed nd play it until playbutton on A side pressed once again.
User prompt
Play trackA music while A side playbutton is active. Stop it when it repressed
User prompt
Play trackA if player pressed playbutton on A side
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'createElement')' in or related to this line: 'var sampleButton1Hidden = document.createElement('div');' Line Number: 8035
User prompt
Add to the game a sample library fullscreen app. it contains all sample music. it should pop up if a samplebutton is pressed
User prompt
ensure pop up a fullscreen sample library selector with 50+ song effect from different instruments if player click on one of the samplebuttons. ✅ Initialize the sample library with 50+ unique sounds. player can select sample songs from this menu
User prompt
ensure pop up a fullscreen sample selector with 50+ song effect from different instruments if player click on one of the samplebuttons.
User prompt
User prompt
function playSampleFromButton(buttonId) { const btn = document.getElementById(buttonId); const sampleSrc = btn.dataset.sample; if (sampleSrc) { const audio = new Audio(sampleSrc); audio.play(); } else { alert("No sample assigned yet!"); } }
User prompt
function openSamplePicker(targetButtonId) { const overlay = document.createElement("div"); overlay.id = "samplePickerOverlay"; overlay.style.position = "fixed"; overlay.style.top = 0; overlay.style.left = 0; overlay.style.width = "100vw"; overlay.style.height = "100vh"; overlay.style.background = "rgba(0,0,0,0.9)"; overlay.style.display = "flex"; overlay.style.flexWrap = "wrap"; overlay.style.justifyContent = "center"; overlay.style.alignItems = "center"; overlay.style.zIndex = "9999"; overlay.style.overflowY = "scroll"; for (let name in sampleLibrary) { const btn = document.createElement("button"); btn.innerText = name; btn.style.margin = "10px"; btn.style.padding = "15px"; btn.style.fontSize = "16px"; btn.style.cursor = "pointer"; btn.style.borderRadius = "12px"; btn.style.background = "#fff"; btn.style.color = "#000"; btn.onclick = () => { document.getElementById(targetButtonId).dataset.sample = sampleLibrary[name]; overlay.remove(); }; overlay.appendChild(btn); } const closeBtn = document.createElement("button"); closeBtn.innerText = "Close"; closeBtn.style.position = "absolute"; closeBtn.style.top = "20px"; closeBtn.style.right = "30px"; closeBtn.style.padding = "10px"; closeBtn.style.background = "#f33"; closeBtn.style.color = "#fff"; closeBtn.onclick = () => overlay.remove(); overlay.appendChild(closeBtn); document.body.appendChild(overlay); }
User prompt
const sampleLibrary = { "kick": "data:audio/wav;base64,//BASE64_KICK==", "snare": "data:audio/wav;base64,//BASE64_SNARE==", "hihat": "data:audio/wav;base64,//BASE64_HIHAT==", "clap": "data:audio/wav;base64,//BASE64_CLAP==", "bass drop": "data:audio/wav;base64,//BASE64_BASSDROP==", "vocal chop": "data:audio/wav;base64,//BASE64_VOCALCHOP==", "piano chord": "data:audio/wav;base64,//BASE64_PIANOCHORD==", "guitar stab": "data:audio/wav;base64,//BASE64_GUITARSTAB==", // ... add 12+ unique sounds };
User prompt
const sampleLibrary = { "kick": "data:audio/wav;base64,//BASE64_KICK==", "snare": "data:audio/wav;base64,//BASE64_SNARE==", "hihat": "data:audio/wav;base64,//BASE64_HIHAT==", "clap": "data:audio/wav;base64,//BASE64_CLAP==", "bass drop": "data:audio/wav;base64,//BASE64_BASSDROP==", "vocal chop": "data:audio/wav;base64,//BASE64_VOCALCHOP==", "piano chord": "data:audio/wav;base64,//BASE64_PIANOCHORD==", "guitar stab": "data:audio/wav;base64,//BASE64_GUITARSTAB==", // ... add 50+ unique sounds };
User prompt
turn off rec button
User prompt
turn off rec button
User prompt
// 🎧 In-app Music Database (Base64 encoded, no external files) const musicDB = { "avicii wake me up": { title: "Avicii – Wake Me Up", data: "data:audio/mpeg;base64,//FAKEBASE64DATA_AVICII==" }, "daft punk one more time": { title: "Daft Punk – One More Time", data: "data:audio/mpeg;base64,//FAKEBASE64DATA_DAFTPUNK==" }, "queen bohemian rhapsody": { title: "Queen – Bohemian Rhapsody", data: "data:audio/mpeg;base64,//FAKEBASE64DATA_QUEEN==" } }; let selectedTrack = null; // 🔍 Virtual keyboard search logic function onVirtualKeyboardInput(text) { const q = text.toLowerCase().trim(); selectedTrack = null; for (let key in musicDB) { if (q.includes(key)) { selectedTrack = musicDB[key]; break; } } const result = selectedTrack ? `🎵 Selected: ${selectedTrack.title}` : `❌ No match found.`; if (typeof FRVR !== "undefined" && FRVR.Display) { FRVR.Display("searchResult", result); } else { document.getElementById("searchResult").innerText = result; } } // ▶️ Load music to the selected deck function loadToDeck(deckId) { if (!selectedTrack) { alert("No track selected!"); return; } const audio = new Audio(selectedTrack.data); audio.controls = true; audio.autoplay = true; const deck = document.getElementById(deckId); if (deck) { deck.innerHTML = ""; deck.appendChild(audio); } } // 🖱️ Bind UI events if (typeof FRVR !== "undefined" && FRVR.Bind) { FRVR.Bind("DeckAButton", "click", () => loadToDeck("DeckA")); FRVR.Bind("DeckBButton", "click", () => loadToDeck("DeckB")); FRVR.Bind("VirtualKeyboard", "input", ev => { onVirtualKeyboardInput(ev.value); }); }
User prompt
Make sure that when the player presses the rec button, it records the player's voice and puts it into the music, into the mix! ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Move rec button right by 50 units
User prompt
Move rec button right by 100 units
User prompt
Move rec button right by 100 units
User prompt
Scale up Rec button size to be the as edit button size under tablet
User prompt
Rec button size should be the same as edit button
User prompt
Move rec button down by 40 units
User prompt
Move rec button down by 50 units
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Playback starts automatically
// Balloon: Animated colorful balloons falling from the top
var Balloon = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var size = 60 + Math.random() * 80; // Random size
	var speedY = 0.8 + Math.random() * 1.2; // Vertical speed
	var speedX = (Math.random() - 0.5) * 2; // Horizontal drift
	var rotationSpeed = (Math.random() - 0.5) * 0.05; // Rotation speed
	// Visuals
	// Use a more realistic width/height ratio for balloons (e.g. 0.55)
	var balloonWidth = size * 0.55;
	var balloonHeight = size;
	var balloonGraphic = self.attachAsset('balloon', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: balloonWidth,
		height: balloonHeight
	});
	// Random color (rainbow)
	var hue = Math.random();
	var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
	balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
	// HSV to RGB helper (essential for color animations)
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t_color = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v;
				g = t_color;
				b = p;
				break;
			case 1:
				r = q;
				g = v;
				b = p;
				break;
			case 2:
				r = p;
				g = v;
				b = t_color;
				break;
			case 3:
				r = p;
				g = q;
				b = v;
				break;
			case 4:
				r = t_color;
				g = p;
				b = v;
				break;
			case 5:
				r = v;
				g = p;
				b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Animation
	self.update = function () {
		self.y += speedY;
		self.x += speedX;
		self.rotation += rotationSpeed;
		// Wrap around if off screen (below the dancing people)
		if (self.y > dancingPeople[0].y + 200) {
			// Wrap below the dancing people
			self.y = -size;
			self.x = Math.random() * 2048; // New random x position
			// Reset size and color for a new balloon
			size = 60 + Math.random() * 80;
			var balloonWidth = size * 0.55;
			var balloonHeight = size;
			balloonGraphic.width = balloonWidth; // Maintain realistic balloon width/height ratio
			balloonGraphic.height = balloonHeight;
			var hue = Math.random();
			var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
			balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
			speedY = 0.8 + Math.random() * 1.2;
			speedX = (Math.random() - 0.5) * 2;
			rotationSpeed = (Math.random() - 0.5) * 0.05;
		}
	};
	return self;
});
// ConfettiEffect: Animated confetti falling from the top with multiple types
var ConfettiEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numParticles = 200; // Increased from 120 to 200 for much higher density
	var particles = [];
	var particleMinSize = 3.0; // Slightly larger minimum size
	var particleMaxSize = 12.0; // Increased maximum size
	var speedY = 5; // pixels per second
	var speedX = 2; // horizontal drift
	// Define confetti types with more rectangle emphasis
	var confettiTypes = [{
		asset: 'centerCircle',
		name: 'circle'
	}, {
		asset: 'crossfaderTrack',
		name: 'rectangle'
	}, {
		asset: 'crossfaderTrack',
		name: 'rectangle'
	}, {
		asset: 'crossfaderTrack',
		name: 'rectangle'
	}, {
		asset: 'crossfaderTrack',
		name: 'rectangle'
	}, {
		asset: 'beatLight',
		name: 'star'
	}];
	// Create confetti particles
	for (var i = 0; i < numParticles; i++) {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		// Choose random confetti type
		var confettiType = confettiTypes[Math.floor(Math.random() * confettiTypes.length)];
		var particle;
		if (confettiType.name === 'rectangle') {
			// Create rectangular confetti with more variety
			var rectWidth = size * (1.2 + Math.random() * 0.8); // More width variation (1.2x to 2.0x)
			var rectHeight = size * (0.4 + Math.random() * 0.6); // More height variation (0.4x to 1.0x)
			particle = LK.getAsset(confettiType.asset, {
				anchorX: 0.5,
				anchorY: 0.5,
				width: rectWidth,
				height: rectHeight
			});
		} else if (confettiType.name === 'star') {
			// Create star-shaped confetti
			particle = LK.getAsset(confettiType.asset, {
				anchorX: 0.5,
				anchorY: 0.5,
				width: size * 0.8,
				height: size * 0.8
			});
		} else {
			// Create circular confetti
			particle = LK.getAsset(confettiType.asset, {
				anchorX: 0.5,
				anchorY: 0.5,
				width: size,
				height: size
			});
		}
		// Random color (rainbow)
		var hue = Math.random();
		var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
		particle.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		// Random initial position in upper half, above screen for seamless loop
		var px = Math.random() * 2048;
		var py = -particleMaxSize - Math.random() * 500;
		particle.x = px;
		particle.y = py;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.8 + Math.random() * 0.4,
			// particles fall at slightly different speeds
			phase: Math.random() * Math.PI * 2,
			type: confettiType.name,
			baseSize: size,
			rotationSpeed: (Math.random() - 0.5) * 0.3 // Random rotation speed
		});
	}
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v, g = t, b = p;
				break;
			case 1:
				r = q, g = v, b = p;
				break;
			case 2:
				r = p, g = v, b = t;
				break;
			case 3:
				r = p, g = q, b = v;
				break;
			case 4:
				r = t, g = p, b = v;
				break;
			case 5:
				r = v, g = p, b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Animation
	self.update = function () {
		var t = Date.now() * 0.001;
		for (var i = 0; i < particles.length; i++) {
			var p = particles[i];
			// Move particles down with horizontal drift
			p.obj.y += speedY * p.speedScale;
			p.obj.x += speedX * Math.sin(t + p.phase);
			// Wrap around vertically (from bottom to top)
			if (p.obj.y > 2732) {
				p.obj.y = -particleMaxSize;
				p.obj.x = Math.random() * 2048; // new random x position
				// Reset rotation and add tween effect for new particles
				p.obj.rotation = 0;
				tween(p.obj, {
					rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1)
				}, {
					duration: 2000 + Math.random() * 3000,
					easing: tween.linear
				});
			}
			// Enhanced rotation based on type
			if (p.type === 'rectangle') {
				p.obj.rotation += p.rotationSpeed * 1.5; // Rectangles rotate faster
			} else if (p.type === 'star') {
				p.obj.rotation += p.rotationSpeed * 0.8; // Stars rotate slower
			} else {
				p.obj.rotation += p.rotationSpeed; // Circles normal rotation
			}
			// Type-specific animations
			if (p.type === 'rectangle') {
				// Rectangles flutter and tumble more dramatically
				var flutter = 0.8 + 0.4 * Math.sin(t * 5 + p.phase);
				var tumble = 0.9 + 0.2 * Math.cos(t * 3 + p.phase);
				p.obj.scaleX = flutter;
				p.obj.scaleY = tumble;
				// Add extra horizontal drift for rectangles
				p.obj.x += Math.sin(t * 2 + p.phase) * 0.8;
			} else if (p.type === 'star') {
				// Stars twinkle
				var twinkle = 0.9 + 0.2 * Math.sin(t * 6 + p.phase);
				p.obj.scaleX = twinkle;
				p.obj.scaleY = twinkle;
				// Add alpha twinkling
				p.obj.alpha = 0.8 + 0.2 * Math.sin(t * 5 + p.phase);
			} else {
				// Circles flicker size
				var baseSize = p.baseSize;
				var s = baseSize * (0.95 + 0.1 * Math.sin(t * 3 + p.phase));
				p.obj.width = s;
				p.obj.height = s;
			}
		}
	};
	return self;
});
// Crossfader: Controls mix between decks
var Crossfader = Container.expand(function () {
	var self = Container.call(this);
	// State
	self.value = 0.5; // 0 = left, 1 = right
	self.dragging = false;
	// Visuals
	var track = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var knob = self.attachAsset('crossfaderKnob', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	knob.y = 0;
	// Methods
	self.setValue = function (val) {
		self.value = Math.max(0, Math.min(1, val));
		knob.x = (self.value - 0.5) * track.width;
	};
	// Touch events
	self.down = function (x, y, obj) {
		self.dragging = true;
		self.setValue((x + track.width / 2) / track.width);
	};
	self.up = function (x, y, obj) {
		self.dragging = false;
	};
	self.move = function (x, y, obj) {
		if (self.dragging) {
			self.setValue((x + track.width / 2) / track.width);
		}
	};
	return self;
});
// DancingPerson: Animated dancing people (women and men)
var DancingPerson = Container.expand(function (forcedAssetId) {
	var self = Container.call(this);
	// Config
	var assetId;
	self.isUV = false; // Add a property to track if this is a UV person
	if (typeof forcedAssetId === 'string') {
		assetId = forcedAssetId;
		self.isUV = forcedAssetId === 'UVW' || forcedAssetId === 'UWM';
	} else {
		// fallback to random if not provided
		var assets = ['dancingWoman', 'dancingMan'];
		assetId = assets[Math.floor(Math.random() * assets.length)];
	}
	// Visuals
	var personGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 1.0 // Anchor at the bottom center (feet)
	});
	// Animation state
	self.baseY = 0;
	self.phaseOffset = Math.random() * Math.PI * 2; // Random start phase for animation
	// Animation
	self.update = function () {
		var t = Date.now() * 0.002; // Time for animation
		// Simple bouncing and swaying animation
		self.y = self.baseY + Math.sin(t * 3 + self.phaseOffset) * 15; // Bounce up/down
		self.rotation = Math.sin(t * 2 + self.phaseOffset) * 0.08; // Sway left/right
	};
	return self;
});
// DeckPlatter: Represents a turntable platter
var DeckPlatter = Container.expand(function () {
	var self = Container.call(this);
	// State
	self.isActive = false;
	self.isScratching = false;
	self.lastAngle = 0;
	self.rotationOffset = 0;
	self.currentRotation = 0;
	self.track = 'A'; // 'A' or 'B'
	self.playing = true;
	self.scratchSpeed = 0;
	self.lastTouchAngle = null;
	// Visuals
	var platter = self.attachAsset('deckPlatter', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var highlight = self.attachAsset('deckPlatterHighlight', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	highlight.alpha = 0;
	// Move highlight behind the platter by adding it first, then re-adding platter
	self.removeChild(platter);
	self.addChild(platter);
	var label = self.attachAsset('deckLabel', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: 350,
		width: 90,
		height: 30
	});
	// Label text
	var labelTxt = new Text2(self.track, {
		size: 40,
		fill: "#222"
	});
	labelTxt.anchor.set(0.5, 0.5);
	label.addChild(labelTxt);
	// Beat light
	var beatLight = self.attachAsset('beatLight', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: 490
	});
	beatLight.alpha = 0.2;
	// Methods
	self.setActive = function (active) {
		self.isActive = active;
		if (active) {
			highlight.alpha = 0.6;
			highlight.visible = true;
			// Ensure highlight is above platter in display order
			self.removeChild(highlight);
			self.addChild(highlight);
			// Flash effect for better visual feedback
			tween(highlight, {
				alpha: 0.3
			}, {
				duration: 200,
				easing: tween.easeOut
			});
		} else {
			highlight.alpha = 0;
			highlight.visible = false;
		}
	};
	self.setTrack = function (track) {
		self.track = track;
		labelTxt.setText(track);
	};
	self.flashBeat = function () {
		beatLight.alpha = 1;
		tween(beatLight, {
			alpha: 0.2
		}, {
			duration: 200,
			easing: tween.easeOut
		});
	};
	// Touch events
	self.down = function (x, y, obj) {
		self.isScratching = true;
		self.setActive(true);
		// Calculate angle from center using platter dimensions
		var dx = x - platter.width / 2;
		var dy = y - platter.height / 2;
		self.lastTouchAngle = Math.atan2(dy, dx);
		self.scratchSpeed = 0;
		LK.getSound('scratch').play();
	};
	self.up = function (x, y, obj) {
		self.isScratching = false;
		self.setActive(false);
		self.scratchSpeed = 0;
		self.lastTouchAngle = null;
	};
	self.move = function (x, y, obj) {
		if (!self.isScratching) return;
		// Calculate angle from center using platter dimensions
		var dx = x - platter.width / 2;
		var dy = y - platter.height / 2;
		var angle = Math.atan2(dy, dx);
		if (self.lastTouchAngle !== null) {
			var delta = angle - self.lastTouchAngle;
			// Normalize delta
			if (delta > Math.PI) delta -= 2 * Math.PI;
			if (delta < -Math.PI) delta += 2 * Math.PI;
			self.rotationOffset += delta;
			self.scratchSpeed = delta;
		}
		self.lastTouchAngle = angle;
	};
	// Called every tick
	self.update = function () {
		// Determine if this deck should be spinning based on start button state
		var shouldSpin = false;
		if (self.track === 'A' && typeof leftStartButtonOn !== 'undefined' && leftStartButtonOn) {
			shouldSpin = true;
		} else if (self.track === 'B' && typeof rightStartButtonOn !== 'undefined' && rightStartButtonOn) {
			shouldSpin = true;
		}
		// If not scratching, platter rotates at normal speed only if should spin
		if (!self.isScratching && self.playing && shouldSpin) {
			self.rotationOffset += 0.02; // Normal play speed
			self.scratchSpeed = 0.02;
		}
		// Apply rotation
		self.currentRotation = self.rotationOffset;
		self.rotation = self.currentRotation;
	};
	return self;
});
// Discoball: True 3D disco ball illusion with spinning mirrored tiles and shining white light particles
var Discoball = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var ballRadius = 150;
	var ballDiameter = ballRadius * 2;
	var numLat = 13; // latitude tiles (vertical)
	var numLon = 24; // longitude tiles (horizontal)
	var tileSize = ballDiameter / numLat * 0.95;
	var tileGap = tileSize * 0.12;
	var tiles = [];
	var shineParticles = [];
	var numShine = 18;
	var shineRadius = ballRadius * 1.08;
	var shineMinAlpha = 0.25;
	var shineMaxAlpha = 0.95;
	var shineMinSize = tileSize * 0.7;
	var shineMaxSize = tileSize * 1.7;
	var spinY = 0; // radians, for y-axis rotation
	var spinSpeed = 0.012;
	var spinDir = 1;
	// --- Mirrored Tiles Grid ---
	// Render both front and back halves for a full 3D ball illusion
	for (var lat = 0; lat < numLat; lat++) {
		var theta = Math.PI * (lat + 0.5) / numLat; // 0 (top) to PI (bottom)
		var y = Math.cos(theta) * ballRadius;
		var r = Math.sin(theta) * ballRadius;
		for (var lon = 0; lon < numLon; lon++) {
			var phi = 2 * Math.PI * lon / numLon;
			// 3D to 2D projection with y-axis spin
			var x3d = Math.cos(phi) * r;
			var z3d = Math.sin(phi) * r;
			// Y-axis spin
			var x2d = x3d * Math.cos(spinY) - z3d * Math.sin(spinY);
			var z2d = x3d * Math.sin(spinY) + z3d * Math.cos(spinY);
			// Draw both front and back tiles, but make back tiles dimmer and behind
			for (var side = 0; side < 2; side++) {
				// side 0: front (z2d > 0), side 1: back (z2d < 0)
				var isFront = side === 0;
				// Always create both front and back tiles, but only show one at a time
				var tile = LK.getAsset('centerCircle', {
					anchorX: 0.5,
					anchorY: 0.5,
					width: tileSize,
					height: tileSize
				});
				// Mirror color: light gray, with some random sparkle
				var base = 0xbb + Math.floor(Math.random() * 0x22);
				var color = base << 16 | base << 8 | base;
				tile.tint = color;
				// Back tiles are dimmer and more transparent
				if (isFront) {
					tile.alpha = 0.92 - 0.18 * (lat / numLat) + Math.random() * 0.08;
				} else {
					tile.alpha = 0.32 - 0.10 * (lat / numLat) + Math.random() * 0.04;
				}
				// Position
				tile.x = x2d;
				tile.y = y;
				// Simulate 3D: scaleX based on z2d
				tile.scaleX = 1 + 0.18 * (Math.abs(z2d) / ballRadius);
				tile.scaleY = 1;
				// For back tiles, send to back (add first), for front tiles, add last
				if (isFront) {
					self.addChild(tile);
				} else {
					self.addChildAt(tile, 0);
				}
				tiles.push({
					tile: tile,
					lat: lat,
					lon: lon,
					theta: theta,
					phi: phi,
					isFront: isFront
				});
			}
		}
	}
	// --- Specular highlight (white spot) ---
	// Removed the white oval particle in front of the discoball
	// --- Shining white light particles ---
	for (var i = 0; i < numShine; i++) {
		var angle = 2 * Math.PI * i / numShine + Math.random() * 0.2;
		var dist = shineRadius * (0.92 + Math.random() * 0.12);
		var particle = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: shineMinSize + Math.random() * (shineMaxSize - shineMinSize),
			height: shineMinSize + Math.random() * (shineMaxSize - shineMinSize)
		});
		particle.tint = 0xffffff;
		particle.alpha = shineMinAlpha + Math.random() * (shineMaxAlpha - shineMinAlpha);
		particle.x = Math.cos(angle) * dist;
		particle.y = Math.sin(angle) * dist;
		self.addChild(particle);
		shineParticles.push({
			particle: particle,
			baseAngle: angle,
			baseDist: dist,
			phase: Math.random() * Math.PI * 2
		});
	}
	// --- Light beams (background) ---
	var numBeams = 32;
	var beams = [];
	var lightBeamLength = 1200; // Increased from 800 to 1200 for longer beams
	for (var i = 0; i < numBeams; i++) {
		var beam = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 0,
			width: 8,
			height: lightBeamLength
		});
		beam.tint = 0xffffff;
		beam.alpha = 0.12 + Math.random() * 0.28;
		beam.rotation = Math.PI * 2 / numBeams * i + Math.random() * 0.1;
		beams.push(beam);
		self.addChild(beam);
	}
	// --- Animation ---
	self.update = function () {
		// Animate y-axis spin in a full 360-degree circle
		spinY += spinSpeed * spinDir;
		if (spinY > Math.PI * 2) spinY -= Math.PI * 2;
		if (spinY < 0) spinY += Math.PI * 2;
		self._spinY = spinY; // direct assignment for full rotation
		// Update mirrored tiles positions for y-axis spin
		for (var i = 0; i < tiles.length; i++) {
			var t = tiles[i];
			// 3D to 2D projection with y-axis spin
			var x3d = Math.cos(t.phi) * Math.sin(t.theta) * ballRadius;
			var y3d = Math.cos(t.theta) * ballRadius;
			var z3d = Math.sin(t.phi) * Math.sin(t.theta) * ballRadius;
			// Y-axis spin
			var x2d = x3d * Math.cos(self._spinY) - z3d * Math.sin(self._spinY);
			var z2d = x3d * Math.sin(self._spinY) + z3d * Math.cos(self._spinY);
			// For front tiles, show if z2d > 0; for back tiles, show if z2d < 0
			if (t.isFront && z2d > 0 || !t.isFront && z2d < 0) {
				t.tile.visible = true;
				t.tile.x = x2d;
				t.tile.y = y3d;
				t.tile.scaleX = 1 + 0.18 * (Math.abs(z2d) / ballRadius);
				t.tile.scaleY = 1;
				// Flicker for disco effect
				if (t.isFront) {
					t.tile.alpha = 0.88 - 0.18 * (t.lat / numLat) + Math.random() * 0.09;
				} else {
					t.tile.alpha = 0.28 - 0.10 * (t.lat / numLat) + Math.random() * 0.04;
				}
			} else {
				t.tile.visible = false;
			}
		}
		// Animate highlight to move in a small circle for extra 3D effect
		// (highlight removed, nothing to animate here)
		var t = Date.now() * 0.001;
		// Animate shine particles
		for (var i = 0; i < shineParticles.length; i++) {
			var s = shineParticles[i];
			var phase = t * 1.2 + s.phase;
			var r = s.baseDist + Math.sin(phase) * 12;
			s.particle.x = Math.cos(s.baseAngle + Math.sin(phase) * 0.12) * r;
			s.particle.y = Math.sin(s.baseAngle + Math.cos(phase) * 0.12) * r;
			s.particle.alpha = shineMinAlpha + (shineMaxAlpha - shineMinAlpha) * (0.5 + 0.5 * Math.sin(phase * 2 + i));
			var size = shineMinSize + (shineMaxSize - shineMinSize) * (0.5 + 0.5 * Math.cos(phase * 1.5 + i));
			s.particle.width = size;
			s.particle.height = size;
		}
		// Animate beams (spin and flicker)
		for (var i = 0; i < beams.length; i++) {
			var beam = beams[i];
			beam.rotation += 0.015;
			beam.alpha = 0.12 + Math.random() * 0.28;
		}
	};
	return self;
});
// EqualizerBars: Animated frequency visualization bars for screens
var EqualizerBars = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numBars = 12;
	var barWidth = 25 * 1.2;
	var barSpacing = 8;
	var maxBarHeight = 100;
	var minBarHeight = 10;
	var bars = [];
	// Create equalizer bars
	for (var i = 0; i < numBars; i++) {
		var bar = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 1.0,
			width: barWidth,
			height: minBarHeight
		});
		// Color based on frequency range (bass=red, mid=yellow, treble=cyan)
		if (i < 4) {
			bar.tint = 0xff0000; // Bass - red
		} else if (i < 8) {
			bar.tint = 0xffff00; // Mid - yellow
		} else {
			bar.tint = 0x00ffff; // Treble - cyan
		}
		bar.x = (i - (numBars - 1) / 2) * (barWidth + barSpacing);
		bar.y = 0;
		self.addChild(bar);
		bars.push({
			obj: bar,
			phase: Math.random() * Math.PI * 2,
			baseHeight: minBarHeight
		});
	}
	// Animation
	self.update = function () {
		var t = Date.now() * 0.001;
		for (var i = 0; i < bars.length; i++) {
			var b = bars[i];
			// Simulate frequency response with different speeds for different frequency ranges
			var speed = 1.0;
			if (i < 4) speed = 0.8; // Bass moves slower
			else if (i < 8) speed = 1.2; // Mid moves medium
			else speed = 1.8; // Treble moves faster
			// Create realistic frequency bar movement
			var height = minBarHeight + (maxBarHeight - minBarHeight) * (0.3 + 0.7 * Math.abs(Math.sin(t * speed * 2 + b.phase + i * 0.3)));
			b.obj.height = height;
			// Add beat sync flash effect
			if (beatTimer && beatTimer < 100) {
				b.obj.alpha = 0.7 + 0.3 * Math.sin(t * 10 + i);
			} else {
				b.obj.alpha = 0.9;
			}
		}
	};
	return self;
});
// FXButton: Triggers a sound effect
var FXButton = Container.expand(function () {
	var self = Container.call(this);
	// Visuals
	var btn = self.attachAsset('fxButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var txt = new Text2('FX', {
		size: 48,
		fill: "#fff"
	});
	txt.anchor.set(0.5, 0.5);
	btn.addChild(txt);
	// State
	self.cooldown = false;
	// Touch
	self.down = function (x, y, obj) {
		if (self.cooldown) return;
		self.cooldown = true;
		LK.getSound('fx').play();
		tween(btn, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 80,
			onFinish: function onFinish() {
				tween(btn, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 120
				});
			}
		});
		LK.setTimeout(function () {
			self.cooldown = false;
		}, 400);
	};
	return self;
});
// FerrisWheel: Animated slow spinning, color-changing Ferris wheel
var FerrisWheel = Container.expand(function () {
	var self = Container.call(this);
	// Config
	self.wheelRadius = 200;
	self.numCabins = 8;
	self.spinSpeed = 0.002; // Radians per update for wheelAssembly
	self.colorChangeCycleTime = 6000; // ms for a full color cycle for a cabin
	// HSV to RGB helper function (essential for color animations)
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t_color = v * (1 - (1 - f) * s); // Renamed 't' to avoid conflict
		switch (i % 6) {
			case 0:
				r = v;
				g = t_color;
				b = p;
				break;
			case 1:
				r = q;
				g = v;
				b = p;
				break;
			case 2:
				r = p;
				g = v;
				b = t_color;
				break;
			case 3:
				r = p;
				g = q;
				b = v;
				break;
			case 4:
				r = t_color;
				g = p;
				b = v;
				break;
			case 5:
				r = v;
				g = p;
				b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Wheel structure (hub and spokes) - this part will rotate
	self.wheelAssembly = self.addChild(new Container());
	// --- Full Circle (Rim) ---
	// Approximate a circle by placing many small ellipses around the rim
	self.rimSegments = 36;
	self.rimRadius = self.wheelRadius;
	for (var i = 0; i < self.rimSegments; i++) {
		var rimAngle = Math.PI * 2 / self.rimSegments * i;
		var rimX = Math.cos(rimAngle) * self.rimRadius;
		var rimY = Math.sin(rimAngle) * self.rimRadius;
		var rim = self.wheelAssembly.attachAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: 11,
			height: 11,
			x: rimX,
			y: rimY
		});
		rim.tint = 0xffffff;
		rim.alpha = 0.7;
	}
	// Central Hub
	var hub = self.wheelAssembly.attachAsset('ferrisHub', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Spokes
	for (var i = 0; i < self.numCabins; i++) {
		var angle = Math.PI * 2 / self.numCabins * i;
		var spoke = self.wheelAssembly.attachAsset('ferrisSpoke', {
			anchorX: 0.5,
			anchorY: 0,
			// Anchor at the hub-connection point
			height: self.wheelRadius,
			// Spoke length is the wheel radius
			rotation: angle
		});
	}
	// Cabins - these will be positioned along the rim and kept upright
	self.cabins = [];
	for (var i = 0; i < self.numCabins; i++) {
		var cabinAngle = Math.PI * 2 / self.numCabins * i;
		// Create cabin using LK.getAsset and add it as a child to the FerrisWheel container (self)
		var cabin = self.addChild(LK.getAsset('ferrisCabin', {
			anchorX: 0.5,
			anchorY: 0.5
		}));
		cabin.initialAngle = cabinAngle; // Store its angular position on the wheel
		// Initial position and color
		var xPos = Math.cos(cabinAngle) * self.wheelRadius;
		var yPos = Math.sin(cabinAngle) * self.wheelRadius;
		cabin.x = xPos;
		cabin.y = yPos;
		var initialHue = cabin.initialAngle / (Math.PI * 2) % 1.0;
		var initialRgb = hsvToRgb(initialHue, 0.85, 0.95);
		cabin.tint = initialRgb[0] << 16 | initialRgb[1] << 8 | initialRgb[2];
		self.cabins.push(cabin);
	}
	self.update = function () {
		// Spin the wheel assembly
		self.wheelAssembly.rotation += self.spinSpeed;
		// Update cabin positions and colors
		var globalTimeFactor = Date.now() % self.colorChangeCycleTime / self.colorChangeCycleTime;
		for (var i = 0; i < self.cabins.length; i++) {
			var cabin = self.cabins[i];
			// Calculate current angle of the cabin attachment point on the rotating wheel
			var currentAttachmentAngle = cabin.initialAngle + self.wheelAssembly.rotation;
			// Update cabin position relative to the FerrisWheel container's center
			cabin.x = Math.cos(currentAttachmentAngle) * self.wheelRadius;
			cabin.y = Math.sin(currentAttachmentAngle) * self.wheelRadius;
			// Cabins are direct children of 'self' (FerrisWheel container).
			// 'self' itself is not rotating, so cabins remain upright (rotation = 0) by default.
			// Animate color: Cycle hue for each cabin
			var cabinHue = (cabin.initialAngle / (Math.PI * 2) + globalTimeFactor) % 1.0;
			var rgb = hsvToRgb(cabinHue, 0.85, 0.95);
			var newTint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
			// Tween for smooth color change
			if (cabin.tint !== newTint) {
				tween.stop(cabin, {
					tint: true
				}); // Stop existing tint tweens on this cabin
				tween(cabin, {
					tint: newTint
				}, {
					duration: 300,
					easing: tween.linear
				});
			}
		}
	};
	return self;
});
// FireworksEffect: Animated fireworks exploding in the upper half of the screen
var FireworksEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numRockets = 8; // Number of simultaneous rockets/explosions - increased from 4 to 8
	var rocketInterval = 800; // ms between new rockets - reduced from 1500 to 800
	var rocketTimer = 0;
	var lastTickTime = Date.now();
	var rockets = [];
	var explosionParticles = [];
	// Predefined rainbow colors for explosions
	var beamColors = [0xff0000,
	// red
	0xff8000,
	// orange
	0xffff00,
	// yellow
	0x00ff00,
	// green
	0x00ffff,
	// cyan
	0x0000ff,
	// blue
	0x8000ff,
	// violet
	0xff00ff // magenta
	];
	// Explosion particle configuration
	var particleMinSize = 8;
	var particleMaxSize = 20;
	var particleSpeed = 15; // pixels per update
	var particleLifetime = 1000; // ms
	// Create an explosion
	function createExplosion(x, y, color) {
		var numParticles = 20 + Math.random() * 10;
		for (var i = 0; i < numParticles; i++) {
			var angle = Math.random() * Math.PI * 2;
			var speed = particleSpeed * (0.8 + Math.random() * 0.4);
			var particle = LK.getAsset('centerCircle', {
				anchorX: 0.5,
				anchorY: 0.5,
				width: (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.5,
				height: (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.5
			});
			particle.tint = color;
			particle.alpha = 1.0;
			particle.x = x;
			particle.y = y;
			self.addChild(particle);
			explosionParticles.push({
				obj: particle,
				speedX: Math.cos(angle) * speed,
				speedY: Math.sin(angle) * speed,
				startTime: Date.now(),
				color: color
			});
		}
	}
	// Animation
	self.update = function () {
		var now = Date.now();
		var delta = now - lastTickTime;
		lastTickTime = now;
		// --- Manage rockets ---
		rocketTimer += delta;
		if (rocketTimer >= rocketInterval && rockets.length < numRockets) {
			rocketTimer -= rocketInterval;
			// Launch a new rocket from the bottom
			var startX = 200 + Math.random() * (2048 - 400);
			var endY = 200 + Math.random() * (600 - 200); // Explode in upper half
			var rocket = LK.getAsset('centerCircle', {
				anchorX: 0.5,
				anchorY: 0.5,
				width: 10,
				height: 10
			});
			rocket.tint = 0xffffff; // White trail
			rocket.alpha = 0.8;
			rocket.x = startX;
			rocket.y = 2732; // Start from bottom
			self.addChild(rocket);
			rockets.push({
				obj: rocket,
				startX: startX,
				endY: endY,
				speedY: -(2732 - endY) / (rocketInterval * 0.6) * 16,
				// Adjust speed based on distance and interval
				color: beamColors[Math.floor(Math.random() * beamColors.length)],
				// Random explosion color from beamColors
				launchedTime: now
			});
		}
		// Update rockets
		for (var i = rockets.length - 1; i >= 0; i--) {
			var rocket = rockets[i];
			rocket.obj.y += rocket.speedY * (delta / 16); // Move rocket based on delta
			// Check if rocket reached explosion point
			if (rocket.obj.y <= rocket.endY || now - rocket.launchedTime > rocketInterval * 0.8) {
				// Explode also after a certain time
				createExplosion(rocket.obj.x, rocket.obj.y, rocket.color);
				rocket.obj.destroy();
				rockets.splice(i, 1);
			}
		}
		// Update explosion particles
		for (var i = explosionParticles.length - 1; i >= 0; i--) {
			var p = explosionParticles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				explosionParticles.splice(i, 1);
			} else {
				// Move particle
				p.obj.x += p.speedX * (delta / 16);
				p.obj.y += p.speedY * (delta / 16);
				// Fade out
				p.obj.alpha = 1.0 - elapsed / particleLifetime;
			}
		}
	};
	return self;
});
// Flamethrower: Animated flames from the ground
var Flamethrower = Container.expand(function () {
	var self = Container.call(this);
	// Visuals - Flamethrower base
	var base = self.attachAsset('flamethrowerBase', {
		anchorX: 0.5,
		anchorY: 1.0,
		scaleX: 0.5,
		scaleY: 0.5
	});
	base.y = 0; // Position at the base of the container
	// Config
	var numParticles = 20;
	var particles = [];
	var particleMinSize = 40;
	var particleMaxSize = 120;
	var speedY = -15; // pixels per second (upwards)
	var speedX = 4; // horizontal flicker/sway
	var particleLifetime = 500; // ms
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v, g = t, b = p;
				break;
			case 1:
				r = q, g = v, b = p;
				break;
			case 2:
				r = p, g = v, b = t;
				break;
			case 3:
				r = p, g = q, b = v;
				break;
			case 4:
				r = t, g = p, b = v;
				break;
			case 5:
				r = v, g = p, b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Create flame particles
	self.createFlame = function () {
		var baseSize = (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.48;
		var size = baseSize;
		// Modify size based on fire effect mode
		if (typeof fireEffectMode !== 'undefined' && fireEffectMode === 2) {
			// Big flames mode - make particles larger
			size = baseSize * 2.5;
		}
		var particle = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size,
			height: size
		});
		// Flame color (yellow to red)
		var hue = Math.random() * 0.1 + 0.05; // Hue from 0.05 (reddish-orange) to 0.15 (yellowish-orange)
		var rgb = hsvToRgb(hue, 1, 1);
		particle.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		// Initial position at the base of the flamethrower
		// Position the particle relative to the top-center of the base graphic
		particle.x = 0; // Relative to the Flamethrower container's origin
		particle.y = -base.height; // Position above the base graphic
		particle.alpha = 0.8 + Math.random() * 0.2;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.8 + Math.random() * 0.4,
			startTime: Date.now(),
			baseSize: size,
			phase: Math.random() * Math.PI * 2
		});
	};
	// Animation
	self.update = function () {
		var now = Date.now();
		var delta = now - (self._lastTickTime || now);
		self._lastTickTime = now;
		// Create new particles periodically based on fire effect mode
		var targetParticleCount = numParticles;
		if (typeof fireEffectMode !== 'undefined') {
			if (fireEffectMode === 0) {
				// Off mode - no flames
				targetParticleCount = 0;
			} else if (fireEffectMode === 2) {
				// Big flames mode - more particles
				targetParticleCount = numParticles * 2;
			}
		}
		if (particles.length < targetParticleCount) {
			self.createFlame();
		}
		// Update particles
		for (var i = particles.length - 1; i >= 0; i--) {
			var p = particles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				particles.splice(i, 1);
			} else {
				// Move particle upwards with horizontal flicker
				p.obj.y += speedY * p.speedScale * (delta / 16);
				p.obj.x += speedX * Math.sin(now * 0.003 + p.phase) * p.speedScale * (delta / 16);
				// Fade out
				p.obj.alpha = (1.0 - elapsed / particleLifetime) * (0.8 + 0.2 * Math.sin(now * 0.004 + p.phase)); // Add flicker to fade
				// Scale down over time
				var size = p.baseSize * (1.0 - elapsed / particleLifetime * 0.8);
				p.obj.width = size;
				p.obj.height = size;
				// Animate color slightly towards red as it fades
				var fadeHue = Math.random() * 0.05; // Shift slightly towards red (hue 0)
				var fadeRgb = hsvToRgb(fadeHue, 1, 1);
				var originalTint = p.obj.tint;
				var r1 = originalTint >> 16 & 0xFF;
				var g1 = originalTint >> 8 & 0xFF;
				var b1 = originalTint & 0xFF;
				var r2 = fadeRgb[0];
				var g2 = fadeRgb[1];
				var b2 = fadeRgb[2];
				var blend = elapsed / particleLifetime;
				var r = Math.round(r1 * (1 - blend) + r2 * blend);
				var g = Math.round(g1 * (1 - blend) + g2 * blend);
				var b = Math.round(b1 * (1 - blend) + b2 * blend);
				p.obj.tint = r << 16 | g << 8 | b;
			}
		}
	};
	return self;
});
// FogEffect: Animated fog in upper half of the map
var FogEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numParticles = 20;
	var particles = [];
	var particleMinSize = 200;
	var particleMaxSize = 800;
	var speedX = 3; // pixels per second, increased horizontal speed
	var speedY = 0.2; // pixels per second, decreased vertical speed
	var particleAlpha = 0.18; // Increased particle alpha for denser fog
	// Create fog particles
	for (var i = 0; i < numParticles; i++) {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		var particle = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size * 2.0,
			// Make particles significantly wider
			height: size * 0.4 // Make particles much flatter
		});
		particle.tint = 0xffffff; // white fog
		particle.alpha = particleAlpha;
		// Random initial position in upper half, extending slightly beyond bounds for seamless loop
		var px = Math.random() * (2048 + particleMaxSize) - particleMaxSize / 2;
		var py = 100 + Math.random() * (1366 - 100);
		particle.x = px;
		particle.y = py;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.5 + Math.random() * 1.0,
			// particles move at different speeds
			baseAlpha: particleAlpha
		});
	}
	// Animation
	self.update = function () {
		// Only animate if fog animation is active
		if (typeof fogAnimationActive !== 'undefined' && !fogAnimationActive) {
			// Hide all particles when fog animation is not active
			for (var i = 0; i < particles.length; i++) {
				var p = particles[i];
				p.obj.alpha = 0;
			}
			return;
		}
		var t = Date.now() * 0.001;
		for (var i = 0; i < particles.length; i++) {
			var p = particles[i];
			// Move particles from right to left
			p.obj.x -= speedX * p.speedScale;
			p.obj.y += speedY * p.speedScale;
			// Wrap around horizontally (from left to right)
			if (p.obj.x < -particleMaxSize / 2) {
				p.obj.x = 2048 + particleMaxSize / 2;
				p.obj.y = Math.random() * (2732 / 2 - 100) + 100; // new random y position within upper half
			}
			// Animate alpha for subtle flow and density changes
			p.obj.alpha = p.baseAlpha + 0.08 * Math.sin(t * 0.8 + i); // Increased alpha animation range
		}
	};
	return self;
});
// KeyboardApp: Simple keyboard app for the tablet
var KeyboardApp = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var keyboardWidth = 742;
	var keyboardHeight = 250;
	var keyWidth = 50;
	var keyHeight = 50;
	var keySpacing = 8;
	var keys = [];
	var keyLabels = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']];
	var shiftLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'], ['z', 'x', 'c', 'v', 'b', 'n', 'm']];
	var symbolLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], [',', '-', '.', '/', '+', ':', ';', '<', '>'], ['_', '=', '|', '\\', '{', '}', '[', ']'], ['é', 'ú', 'ü', 'ű', 'í', 'ó', 'ö', 'ő', 'á']];
	self.isSymbolsMode = false; // Track if symbols mode is active
	self.isShiftMode = false; // Track if shift mode is active
	// Create keyboard background
	var keyboardBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: keyboardWidth,
		height: keyboardHeight
	});
	keyboardBg.tint = 0x333333;
	// Create keys
	var startX = (keyboardWidth - (keyLabels[0].length * (keyWidth + keySpacing) - keySpacing)) / 2;
	var startY = 20;
	for (var row = 0; row < keyLabels.length; row++) {
		var rowLabels = keyLabels[row];
		var rowStartX = (keyboardWidth - (rowLabels.length * (keyWidth + keySpacing) - keySpacing)) / 2;
		for (var col = 0; col < rowLabels.length; col++) {
			var keyBg = self.attachAsset('crossfaderTrack', {
				anchorX: 0.5,
				anchorY: 0.5,
				width: keyWidth,
				height: keyHeight
			});
			keyBg.tint = 0x555555;
			keyBg.x = rowStartX + col * (keyWidth + keySpacing) + keyWidth / 2;
			keyBg.y = startY + row * (keyHeight + keySpacing) + keyHeight / 2;
			var keyText = new Text2(rowLabels[col], {
				size: 40,
				fill: 0xFFFFFF
			});
			keyText.anchor.set(0.5, 0.5);
			keyBg.addChild(keyText);
			// Add basic press feedback
			keyBg.down = function (x, y, obj) {
				self.handleKeyPress(keyText.text); // Pass the key label to a new handler
				tween(this, {
					scaleX: 1.1,
					scaleY: 1.1
				}, {
					duration: 80
				});
				// Add orange flashing effect using tint and alpha
				// Create a temporary flashing graphic above the key
				var flashGraphic = LK.getAsset('flashRectangle', {
					anchorX: 0.5,
					anchorY: 0.5,
					width: this.width,
					height: this.height
				});
				flashGraphic.alpha = 1.0;
				// Position the flash graphic centered on the key
				flashGraphic.x = this.x;
				flashGraphic.y = this.y;
				self.addChild(flashGraphic);
				// Fade out and remove the flash graphic
				tween(flashGraphic, {
					alpha: 0.0
				}, {
					duration: 200,
					onFinish: function onFinish() {
						flashGraphic.destroy();
					}
				});
				tween(this, {
					tint: 0xff8000,
					alpha: 1.0
				}, {
					duration: 50,
					onFinish: function () {
						tween(this, {
							tint: 0x555555,
							alpha: 0.8 // Slightly faded normal state
						}, {
							duration: 150
						});
					}.bind(this)
				});
				LK.getSound('fx').play(); // Play a generic sound
			};
			keyBg.up = function (x, y, obj) {
				// Return to original tint and alpha on release
				tween(this, {
					tint: 0x555555,
					alpha: 0.8 // Slightly faded normal state
				}, {
					duration: 100
				});
				tween(this, {
					scaleX: 1.0,
					scaleY: 1.0
				}, {
					duration: 80
				});
			};
			keys.push(keyBg);
		}
	}
	// Add space bar
	var spaceBarWidth = keyWidth * 6 + keySpacing * 5;
	var spaceBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: spaceBarWidth * 1.1,
		height: keyHeight
	});
	var spaceGraphic = self.attachAsset('Space', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	spaceGraphic.width = spaceBarWidth * 1.1;
	spaceGraphic.height = keyHeight;
	spaceBarBg.addChild(spaceGraphic);
	spaceBarBg.tint = 0x555555;
	spaceBarBg.x = keyboardWidth / 2;
	spaceBarBg.y = startY + keyLabels.length * (keyHeight + keySpacing) + keyHeight / 2;
	var spaceText = new Text2('SPACE', {
		size: 40,
		fill: 0xFFFFFF
	});
	spaceText.anchor.set(0.5, 0.5);
	spaceBarBg.addChild(spaceText);
	// Add basic press feedback for space bar
	spaceBarBg.down = function (x, y, obj) {
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the spacebar
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the spacebar
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	spaceBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(spaceBarBg);
	// Add symbols button to the left of the space bar
	var symbolsBarWidth = keyWidth * 2 + keySpacing;
	var symbolsBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: symbolsBarWidth,
		height: keyHeight
	});
	symbolsBarBg.tint = 0x555555;
	symbolsBarBg.x = spaceBarBg.x - spaceBarBg.width / 2 - symbolsBarBg.width / 2 - keySpacing - 22;
	symbolsBarBg.y = spaceBarBg.y;
	var symbolsText = new Text2('SYMBOLS', {
		size: 30,
		fill: 0xFFFFFF
	});
	symbolsText.anchor.set(0.5, 0.5);
	symbolsBarBg.addChild(symbolsText);
	// Add basic press feedback for symbols button
	symbolsBarBg.down = function (x, y, obj) {
		// Toggle symbols mode
		self.isSymbolsMode = !self.isSymbolsMode;
		// If switching to symbols mode, turn off shift mode
		if (self.isSymbolsMode) {
			self.isShiftMode = false;
			// Find and update the shift button text
			for (var i = 0; i < keys.length; i++) {
				if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') {
					keys[i].children[0].setText('shift');
					break;
				}
			}
		}
		self.updateKeyLabels(); // Update the labels
		// Update symbols button text based on mode
		symbolsText.setText(self.isSymbolsMode ? 'ABC' : 'SYMBOLS'); // Update symbols button text based on mode
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the symbols button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the symbols button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	symbolsBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(symbolsBarBg);
	// Add Shift button above the symbols button
	var shiftBarWidth = keyWidth * 2 + keySpacing;
	var shiftBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: shiftBarWidth,
		height: keyHeight
	});
	shiftBarBg.tint = 0x555555;
	shiftBarBg.x = symbolsBarBg.x;
	shiftBarBg.y = symbolsBarBg.y - keyHeight - keySpacing;
	var shiftText = new Text2('SHIFT', {
		size: 30,
		fill: 0xFFFFFF
	});
	shiftText.anchor.set(0.5, 0.5);
	shiftBarBg.addChild(shiftText);
	// Add basic press feedback for shift button
	shiftBarBg.down = function (x, y, obj) {
		// Toggle shift mode
		self.isShiftMode = !self.isShiftMode;
		self.updateKeyLabels(); // Update the labels
		// Update shift button text based on mode
		shiftText.setText(self.isShiftMode ? 'SHIFT' : 'shift'); // Maybe change visual later
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the shift button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the shift button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	shiftBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(shiftBarBg);
	// Add X button above the enter button
	var xBarWidth = keyWidth * 1.5;
	var xBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: xBarWidth,
		height: keyHeight
	});
	xBarBg.tint = 0x555555;
	xBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + xBarBg.width / 2 + keySpacing + 15 + 20;
	xBarBg.y = spaceBarBg.y - keyHeight - keySpacing;
	var xText = new Text2('DELETE', {
		size: 28,
		fill: 0xFFFFFF
	});
	xText.anchor.set(0.5, 0.5);
	xBarBg.addChild(xText);
	// Add basic press feedback for DELETE button
	xBarBg.down = function (x, y, obj) {
		self.handleKeyPress('DELETE'); // Handle DELETE/BACKSPACE key press
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the X button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the X button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	xBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(xBarBg);
	// Add enter button to the right of the space bar
	var enterBarWidth = keyWidth * 2 + keySpacing;
	var enterBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: enterBarWidth,
		height: keyHeight
	});
	enterBarBg.tint = 0x555555;
	enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15;
	enterBarBg.y = spaceBarBg.y;
	var enterText = new Text2('ENTER', {
		size: 40,
		fill: 0xFFFFFF
	});
	enterText.anchor.set(0.5, 0.5);
	enterBarBg.addChild(enterText);
	// Add basic press feedback for enter button
	enterBarBg.down = function (x, y, obj) {
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the enter button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the enter button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	enterBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(enterBarBg);
	// Method to update key labels based on mode
	self.updateKeyLabels = function () {
		var currentLabels;
		if (self.isSymbolsMode) {
			currentLabels = symbolLabels;
		} else if (self.isShiftMode) {
			currentLabels = shiftLabels;
		} else {
			currentLabels = keyLabels;
		}
		var labelIndex = 0;
		for (var row = 0; row < currentLabels.length; row++) {
			var rowLabels = currentLabels[row];
			for (var col = 0; col < rowLabels.length; col++) {
				if (labelIndex < keys.length) {
					var keyBg = keys[labelIndex];
					// Find the Text2 child object
					var keyText = null;
					for (var i = 0; i < keyBg.children.length; i++) {
						if (keyBg.children[i] instanceof Text2) {
							keyText = keyBg.children[i];
							break;
						}
					}
					if (keyText && keyText.setText) {
						keyText.setText(rowLabels[col]);
					}
				}
				labelIndex++;
			}
		}
	};
	// Method to handle key press
	self.handleKeyPress = function (key) {
		// Initialize searchText if it hasn't been already
		if (typeof searchText === 'undefined') {
			searchText = new Text2('', {
				size: 28,
				fill: 0x888888
			});
			searchText.anchor.set(0.5, 0.5);
			// Position placeholder, actual position will be set by RekordboxApp
			searchText.x = 0;
			searchText.y = 0;
		}
		console.log("Key pressed:", key);
		// Add logic here to handle different key presses, e.g., appending to a text input
		if (key === 'ENTER') {
			// Handle enter key press
			// For now, do nothing specific, the enter button handler does the selection
		}
	};
	symbolsBarBg.down = function (x, y, obj) {
		// Toggle symbols mode
		self.isSymbolsMode = !self.isSymbolsMode;
		self.updateKeyLabels(); // Update the labels
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the symbols button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the symbols button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	symbolsBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(symbolsBarBg);
	// Add enter button to the right of the space bar
	var enterBarWidth = keyWidth * 2 + keySpacing;
	var enterBarBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: enterBarWidth,
		height: keyHeight
	});
	enterBarBg.tint = 0x555555;
	enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15;
	enterBarBg.y = spaceBarBg.y;
	var enterText = new Text2('ENTER', {
		size: 40,
		fill: 0xFFFFFF
	});
	enterText.anchor.set(0.5, 0.5);
	enterBarBg.addChild(enterText);
	// Add basic press feedback for enter button
	enterBarBg.down = function (x, y, obj) {
		self.handleKeyPress('ENTER'); // Handle enter key press
		// Reset shift and symbols mode after enter
		self.isShiftMode = false;
		self.isSymbolsMode = false;
		self.updateKeyLabels();
		// Find and update the symbols button text
		for (var i = 0; i < keys.length; i++) {
			if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2) {
				if (keys[i].children[0].text === 'ABC' || keys[i].children[0].text === 'SYMBOLS') {
					// Find the symbols button and update its text
					keys[i].children[0].setText('SYMBOLS');
					break;
				}
			}
		}
		// Find and update the shift button text
		for (var i = 0; i < keys.length; i++) {
			if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') {
				keys[i].children[0].setText('shift');
				break;
			}
		}
		tween(this, {
			scaleX: 1.05,
			scaleY: 1.05
		}, {
			duration: 80
		});
		// Add orange flashing effect using tint and alpha
		// Create a temporary flashing graphic above the enter button
		var flashGraphic = LK.getAsset('flashRectangle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: this.width,
			height: this.height
		});
		flashGraphic.alpha = 1.0;
		// Position the flash graphic centered on the enter button
		flashGraphic.x = this.x;
		flashGraphic.y = this.y;
		self.addChild(flashGraphic);
		// Fade out and remove the flash graphic
		tween(flashGraphic, {
			alpha: 0.0
		}, {
			duration: 200,
			onFinish: function onFinish() {
				flashGraphic.destroy();
			}
		});
		tween(this, {
			tint: 0xff8000,
			alpha: 1.0
		}, {
			duration: 50
		});
		LK.getSound('fx').play(); // Play a generic sound
	};
	enterBarBg.up = function (x, y, obj) {
		// Return to original tint and alpha on release
		tween(this, {
			tint: 0x555555,
			alpha: 0.8 // Slightly faded normal state
		}, {
			duration: 100
		});
		tween(this, {
			scaleX: 1.0,
			scaleY: 1.0
		}, {
			duration: 80
		});
	};
	keys.push(enterBarBg);
	return self;
});
// LaserShow: Realistic party laser show with moving heads and multi-color animated beams
var LaserShow = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numHeads = 6;
	var beamsPerHead = 3;
	var headRadius = 700; // Reduced from 1100 to 700 to move heads closer to the middle
	var centerX = 2048 / 2;
	var centerY = 400;
	var beamLength = 1200; // Reduced from 1600 to 1200 to keep beams inside the new head radius
	var beamWidth = 18;
	var beamAlpha = 0.65;
	var headAlpha = 0.7;
	var heads = [];
	var beams = [];
	// Predefined rainbow colors for beams
	var beamColors = [0xff0000,
	// red
	0xff8000,
	// orange
	0xffff00,
	// yellow
	0x00ff00,
	// green
	0x00ffff,
	// cyan
	0x0000ff,
	// blue
	0x8000ff,
	// violet
	0xff00ff // magenta
	];
	// Create moving heads and beams
	for (var h = 0; h < numHeads; h++) {
		// Head position in a semi-circle
		var angle = Math.PI * (h + 1) / (numHeads + 1);
		var hx = centerX + Math.cos(angle) * headRadius;
		var hy = centerY + Math.sin(angle) * 200;
		// Head (visual: ellipse, colored)
		// Use rainbowEllipse for gradient rainbow look
		var head = LK.getAsset('rainbowEllipse', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: hx,
			y: hy,
			width: 20,
			height: 20
		});
		head.alpha = headAlpha;
		// Animate color: store initial hue offset for each head
		head._rainbowHueOffset = h * (360 / numHeads);
		self.addChild(head);
		heads.push(head);
		// Beams for this head
		for (var b = 0; b < beamsPerHead; b++) {
			var beam = LK.getAsset('laserBeam', {
				anchorX: 0.5,
				anchorY: 0,
				x: hx,
				y: hy,
				width: beamWidth,
				height: beamLength
			});
			// Assign color in a rainbow pattern, offset by head and beam
			beam.tint = beamColors[(h * beamsPerHead + b) % beamColors.length];
			beam.alpha = beamAlpha;
			// Initial rotation
			beam.rotation = 0;
			self.addChild(beam);
			beams.push({
				obj: beam,
				headIdx: h,
				beamIdx: b,
				baseAngle: angle
			});
		}
	}
	// Animate heads and beams
	self.update = function () {
		var t = Date.now() * 0.001;
		// Only update head colors every few frames to improve performance
		if (LK.ticks % 4 === 0) {
			// Animate heads in a subtle up/down and color pulse
			for (var h = 0; h < heads.length; h++) {
				var head = heads[h];
				// Sway up/down
				head.y = centerY + Math.sin(Math.PI * (h + 1) / (numHeads + 1)) * 200 + Math.sin(t * 1.2 + h) * 18;
				// Pulse alpha
				head.alpha = headAlpha + 0.15 * Math.sin(t * 2 + h);
				// Animate rainbow color: cycle hue every second
				// Full cycle every 1 second (t mod 1.0)
				var hue = (head._rainbowHueOffset + t * 360) % 360 / 360;
				// HSV to RGB conversion
				var i = Math.floor(hue * 6);
				var f = hue * 6 - i;
				var q = 1 - f;
				var tcol = 1 - (1 - f);
				var r, g, b;
				switch (i % 6) {
					case 0:
						r = 1, g = tcol, b = 0;
						break;
					case 1:
						r = q, g = 1, b = 0;
						break;
					case 2:
						r = 0, g = 1, b = tcol;
						break;
					case 3:
						r = 0, g = q, b = 1;
						break;
					case 4:
						r = tcol, g = 0, b = 1;
						break;
					case 5:
						r = 1, g = 0, b = q;
						break;
				}
				var tint = Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255);
				// Only tween if color actually changed
				if (head.tint !== tint) {
					head.tint = tint;
				}
			}
		}
		// Animate beams: sweep, flicker, and color pulse
		for (var i = 0; i < beams.length; i++) {
			var beamData = beams[i];
			var beam = beamData.obj;
			var h = beamData.headIdx;
			var b = beamData.beamIdx;
			var baseAngle = beamData.baseAngle;
			// Beam origin follows head
			var head = heads[h];
			beam.x = head.x;
			beam.y = head.y;
			// Check laser show mode (controlled by second effect button)
			if (typeof laserShowMode !== 'undefined' && laserShowMode === 1) {
				// Slow laser show mode - beams visible
				beam.visible = true;
				var sweep = Math.sin(t * 1.2 + h + b * 0.5) * 0.3 + Math.sin(t * 0.8 + b) * 0.15;
				var beatPulse = Math.sin(t * 2.8 + h + b) * 0.12;
				beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse;
				// Slow beam length flicker
				var flicker = 1 + 0.18 * Math.sin(t * 3.5 + h * 2 + b * 1.2 + Math.sin(t * 2.0 + b));
				beam.height = beamLength * flicker;
				// Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color
				if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
					// UV mode active - different colors per beam
					var colorIdx = (h * beamsPerHead + b + Math.floor(t * 2.5 + h + b)) % beamColors.length;
					beam.tint = beamColors[colorIdx];
				} else {
					// Only laser mode - all beams same color but cycle through gradient
					var sameColorIdx = Math.floor(t * 2.5) % beamColors.length;
					beam.tint = beamColors[sameColorIdx];
				}
				// Slow alpha strobe effect
				beam.alpha = beamAlpha + 0.2 * Math.abs(Math.sin(t * 5 + h + b));
			} else if (typeof laserShowMode !== 'undefined' && laserShowMode === 2) {
				// Fast laser show mode - beams visible
				beam.visible = true;
				var sweep = Math.sin(t * 2.4 + h + b * 0.9) * 0.5 + Math.sin(t * 1.8 + b) * 0.25;
				var beatPulse = Math.sin(t * 6.0 + h + b) * 0.2;
				beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse;
				// Fast beam length flicker
				var flicker = 1 + 0.35 * Math.sin(t * 7 + h * 2 + b * 1.8 + Math.sin(t * 3.5 + b));
				beam.height = beamLength * flicker;
				// Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color
				if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
					// UV mode active - different colors per beam
					var colorIdx = (h * beamsPerHead + b + Math.floor(t * 6 + h + b)) % beamColors.length;
					beam.tint = beamColors[colorIdx];
				} else {
					// Only laser mode - all beams same color but cycle through gradient
					var sameColorIdx = Math.floor(t * 6) % beamColors.length;
					beam.tint = beamColors[sameColorIdx];
				}
				// Fast alpha strobe effect
				beam.alpha = beamAlpha + 0.4 * Math.abs(Math.sin(t * 12 + h + b));
			} else {
				// Off mode - hide beams completely
				beam.visible = false;
			}
		}
	};
	return self;
});
// MoneyParticle: Animated money bills falling from the top
var MoneyParticle = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var size = (40 + Math.random() * 30) / 1.2; // Random size between 40-70
	var speedY = 1.2 + Math.random() * 0.8; // Vertical speed
	var speedX = (Math.random() - 0.5) * 1.5; // Horizontal drift
	var rotationSpeed = (Math.random() - 0.5) * 0.03; // Rotation speed
	// Create money bill graphic using money asset
	var moneyGraphic = self.attachAsset('money', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: size * 1.8,
		// Money bills are wider than tall
		height: size
	});
	// Keep natural money asset color (remove tint)
	// Animation
	self.update = function () {
		self.y += speedY;
		self.x += speedX;
		self.rotation += rotationSpeed;
		// Wrap around if off screen (below the dancing people)
		if (self.y > dancingPeople[0].y + 200) {
			// Reset to top with new random properties
			self.y = -size;
			self.x = Math.random() * 2048;
			// Reset size and properties for variety
			size = 40 + Math.random() * 30;
			moneyGraphic.width = size * 1.8;
			moneyGraphic.height = size;
			speedY = 1.2 + Math.random() * 0.8;
			speedX = (Math.random() - 0.5) * 1.5;
			rotationSpeed = (Math.random() - 0.5) * 0.03;
			// Animate scale for sparkle effect instead of tint
			tween(moneyGraphic, {
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 300,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(moneyGraphic, {
						scaleX: 1.0,
						scaleY: 1.0
					}, {
						duration: 300,
						easing: tween.easeIn
					});
				}
			});
		}
	};
	return self;
});
// PianoRoll: Interactive piano roll for composing music
var PianoRoll = Container.expand(function (config) {
	var self = Container.call(this);
	// Config
	self.config = config;
	self.notes = []; // Array to store notes {beatIndex, keyIndex, visual, pitch}
	self.instruments = ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings']; // Added more instruments
	var numKeys = Math.floor(config.height / config.noteHeight);
	var keyLabels = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
	// Piano Roll Background
	var pianoRollBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: config.width,
		height: config.height
	});
	pianoRollBg.tint = 0x2a2a2a;
	pianoRollBg.x = 0; // Relative to container
	pianoRollBg.y = 0; // Relative to container
	// Piano Keyboard (left side)
	var keyboardBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: config.keyboardWidth,
		height: config.height
	});
	keyboardBg.tint = 0x000000;
	keyboardBg.x = 0; // Relative to container
	keyboardBg.y = 0; // Relative to container
	// Create piano keys
	var keys = [];
	for (var i = 0; i < numKeys; i++) {
		var keyY = i * config.noteHeight;
		var keyNote = keyLabels[i % 12];
		var isBlackKey = keyNote.indexOf('#') !== -1;
		var key = self.attachAsset('crossfaderTrack', {
			anchorX: 0,
			anchorY: 0,
			width: config.keyboardWidth - 2,
			height: config.noteHeight - 1
		});
		key.tint = isBlackKey ? 0x333333 : 0xFFFFFF;
		key.x = 1;
		key.y = keyY;
		self.addChild(key);
		// Key label
		var keyText = new Text2(keyNote + Math.floor(i / 12 + 3), {
			size: 16,
			fill: isBlackKey ? 0xFFFFFF : 0x000000
		});
		keyText.anchor.set(0, 0.5);
		keyText.x = key.x + 5;
		keyText.y = key.y + config.noteHeight / 2;
		self.addChild(keyText);
		// Key interaction
		key.keyIndex = i;
		key.note = keyNote + Math.floor(i / 12 + 3);
		key.down = function (x, y, obj) {
			// Play note sound - placeholder for now
			console.log("Key pressed:", this.note);
			// Visual feedback
			var originalTint = this.tint;
			this.tint = 0xFF6600;
			var keyRef = this;
			LK.setTimeout(function () {
				keyRef.tint = originalTint;
			}, 200);
		};
		keys.push(key);
	}
	// Grid lines
	var gridContainer = new Container();
	self.addChild(gridContainer);
	var totalBars = 16; // Example: 16 bars
	var beatsPerBar = 4; // Example: 4 beats per bar
	var timelineWidth = config.width - config.keyboardWidth;
	var beatWidth = timelineWidth / (totalBars * beatsPerBar);
	// Horizontal grid lines (for each note)
	for (var i = 0; i <= numKeys; i++) {
		var gridY = i * config.noteHeight;
		var hLine = LK.getAsset('crossfaderTrack', {
			anchorX: 0,
			anchorY: 0,
			width: timelineWidth,
			height: 1
		});
		hLine.tint = config.gridColor;
		hLine.x = config.keyboardWidth;
		hLine.y = gridY;
		gridContainer.addChild(hLine);
	}
	// Vertical grid lines (for each beat)
	for (var i = 0; i <= totalBars * beatsPerBar; i++) {
		var gridX = config.keyboardWidth + i * beatWidth;
		var vLine = LK.getAsset('crossfaderTrack', {
			anchorX: 0,
			anchorY: 0,
			width: 1,
			height: config.height
		});
		vLine.tint = i % beatsPerBar === 0 ? 0x555555 : config.gridColor;
		vLine.x = gridX;
		vLine.y = 0; // Relative to container
		gridContainer.addChild(vLine);
	}
	// Note placement area for interaction
	var noteArea = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: timelineWidth,
		height: config.height
	});
	noteArea.tint = 0x1a1a1a;
	noteArea.alpha = 0.001; // Make it almost transparent but interactive
	noteArea.x = config.keyboardWidth;
	noteArea.y = 0; // Relative to container
	self.addChild(noteArea);
	// Variables for dragging instrument voices
	var draggedInstrument = null;
	var dragOffset = {
		x: 0,
		y: 0
	};
	// Instrument voice selection (using instruments from SongMakerApp)
	var instrumentVoices = self.config.instruments || ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings'];
	var instrumentButtons = [];
	var instrumentButtonY = 50; // Position buttons relative to instrumentsToolsContainer
	var instrumentsPerRow = 4; // Number of instrument buttons per row
	var buttonSpacing = 20;
	for (var i = 0; i < instrumentVoices.length; i++) {
		var button = self.attachAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: 150,
			height: 60
		});
		var row = Math.floor(i / instrumentsPerRow);
		var col = i % instrumentsPerRow;
		button.tint = config.noteColors[i % config.noteColors.length];
		// Position buttons relative to the instrumentsToolsContainer
		button.x = config.keyboardWidth + 100 + col * (button.width + buttonSpacing + 80); // Add extra 80px spacing
		button.y = instrumentButtonY + row * (button.height + buttonSpacing); // Arrange in rows
		self.addChild(button);
		var buttonText = new Text2(instrumentVoices[i], {
			size: 80,
			fill: 0xFFFFFF
		});
		buttonText.anchor.set(0.5, 0.5);
		button.addChild(buttonText);
		button.instrumentVoice = instrumentVoices[i];
		button.down = function (x, y, obj) {
			draggedInstrument = {
				voice: this.instrumentVoice,
				color: this.tint
			};
			dragOffset.x = x;
			dragOffset.y = y;
		};
		instrumentButtons.push(button);
	}
	self.move = function (x, y, obj) {
		if (draggedInstrument) {
			// Convert global touch coordinates to local coordinates relative to PianoRoll container
			var globalPoint = obj && obj.position ? obj.position : {
				x: x,
				y: y
			}; // Get global position from event object with fallback
			var localPoint = self.toLocal(globalPoint);
			// Create a temporary visual representation of the dragged note/instrument voice
			if (!self.draggedNoteVisual) {
				self.draggedNoteVisual = LK.getAsset('crossfaderTrack', {
					anchorX: 0,
					anchorY: 0,
					width: beatWidth - 2,
					height: config.noteHeight - 2
				});
				self.draggedNoteVisual.alpha = 0.7;
				self.addChild(self.draggedNoteVisual);
			}
			self.draggedNoteVisual.tint = draggedInstrument.color;
			self.draggedNoteVisual.x = localPoint.x - dragOffset.x + config.keyboardWidth; // Adjust for drag offset and keyboard width
			self.draggedNoteVisual.y = localPoint.y - dragOffset.y; // Adjust for drag offset
		}
	};
	self.up = function (x, y, obj) {
		if (draggedInstrument) {
			// Convert global touch coordinates to local coordinates relative to PianoRoll container
			var globalPoint = obj && obj.position ? obj.position : {
				x: x,
				y: y
			}; // Get global position from event object with fallback
			var localPoint = self.toLocal(globalPoint);
			// Calculate grid position based on the release position, relative to the noteArea's origin
			var releaseX = localPoint.x - config.keyboardWidth;
			var releaseY = localPoint.y;
			var beatIndex = Math.floor(releaseX / beatWidth);
			var keyIndex = Math.floor(releaseY / config.noteHeight);
			// Check if release position is within the note area
			if (releaseX >= 0 && releaseX < timelineWidth && releaseY >= 0 && releaseY < config.height) {
				if (beatIndex >= 0 && beatIndex < totalBars * beatsPerBar && keyIndex >= 0 && keyIndex < numKeys) {
					// Check if a note of this instrument already exists at this position
					var existingNoteIndex = -1;
					for (var i = 0; i < self.notes.length; i++) {
						if (self.notes[i].beatIndex === beatIndex && self.notes[i].keyIndex === keyIndex && self.notes[i].instrument === draggedInstrument.voice) {
							existingNoteIndex = i;
							break;
						}
					}
					if (existingNoteIndex !== -1) {
						// Remove existing note of the same instrument
						self.removeChild(self.notes[existingNoteIndex].visual);
						self.notes.splice(existingNoteIndex, 1);
					} else {
						// Create new note
						var noteX = config.keyboardWidth + beatIndex * beatWidth;
						var noteY = keyIndex * config.noteHeight;
						var noteVisual = LK.getAsset('crossfaderTrack', {
							anchorX: 0,
							anchorY: 0,
							width: beatWidth - 2,
							height: config.noteHeight - 2
						});
						noteVisual.tint = draggedInstrument.color;
						noteVisual.x = noteX + 1;
						noteVisual.y = noteY + 1;
						self.addChild(noteVisual);
						var note = {
							beatIndex: beatIndex,
							keyIndex: keyIndex,
							visual: noteVisual,
							pitch: keyLabels[keyIndex % 12] + Math.floor(keyIndex / 12 + 3),
							instrument: draggedInstrument.voice // Store the instrument voice
						};
						self.notes.push(note);
						// Play sound for the placed note (placeholder - map instrument voice to sound)
						console.log("Placed note:", note);
						LK.getSound('fx').play(); // Generic sound for now
					}
				}
			}
			// Remove the dragged note visual
			if (self.draggedNoteVisual) {
				self.removeChild(self.draggedNoteVisual);
				self.draggedNoteVisual = null;
			}
			draggedInstrument = null;
			dragOffset = {
				x: 0,
				y: 0
			};
		}
	};
	return self;
});
// RekordboxApp: Full-featured rekordbox application interface for the tablet
var RekordboxApp = Container.expand(function () {
	var self = Container.call(this);
	// Config
	self.currentScreen = 'browser'; // 'browser', 'playlist', 'settings', 'performance'
	self.selectedTrackIndex = 0;
	self.selectedCategoryIndex = 0;
	self.currentCategory = 'all';
	// Track database
	self.tracks = [{
		name: 'Summer Vibes',
		artist: 'DJ Alex',
		bpm: 128,
		key: 'Am',
		genre: 'House'
	}, {
		name: 'Night Drive',
		artist: 'Luna Beat',
		bpm: 132,
		key: 'Fm',
		genre: 'Techno'
	}, {
		name: 'Electric Dreams',
		artist: 'Synth Master',
		bpm: 140,
		key: 'Gm',
		genre: 'Electronic'
	}, {
		name: 'Deep Ocean',
		artist: 'Wave Runner',
		bpm: 124,
		key: 'Dm',
		genre: 'Deep House'
	}, {
		name: 'City Lights',
		artist: 'Urban Sound',
		bpm: 126,
		key: 'Em',
		genre: 'Progressive'
	}, {
		name: 'Midnight Express',
		artist: 'Beat Factory',
		bpm: 130,
		key: 'Cm',
		genre: 'Techno'
	}, {
		name: 'Tropical Storm',
		artist: 'Island Beats',
		bpm: 118,
		key: 'Bm',
		genre: 'Tropical'
	}, {
		name: 'Neon Pulse',
		artist: 'Cyber DJ',
		bpm: 135,
		key: 'F#m',
		genre: 'Electro'
	}, {
		name: 'Sunset Boulevard',
		artist: 'Coast Drive',
		bpm: 122,
		key: 'Am',
		genre: 'Chill'
	}, {
		name: 'Bass Revolution',
		artist: 'Low End Theory',
		bpm: 128,
		key: 'Dm',
		genre: 'Bass'
	}, {
		name: 'Street Anthem',
		artist: 'MC Flow',
		bpm: 90,
		key: 'Em',
		genre: 'Hip-Hop'
	}, {
		name: 'Island Rhythm',
		artist: 'Rasta King',
		bpm: 75,
		key: 'Am',
		genre: 'Reggae'
	}, {
		name: 'Smooth Jazz Night',
		artist: 'Jazz Collective',
		bpm: 110,
		key: 'Bbm',
		genre: 'Jazz'
	}, {
		name: 'Symphony No. 9',
		artist: 'Classical Ensemble',
		bpm: 120,
		key: 'Dm',
		genre: 'Classical'
	}, {
		name: 'Rock The House',
		artist: 'Thunder Strike',
		bpm: 140,
		key: 'Em',
		genre: 'EDM'
	}, {
		name: 'Pop Star',
		artist: 'Chart Topper',
		bpm: 125,
		key: 'Am',
		genre: 'Pop'
	}, {
		name: 'Floating Dreams',
		artist: 'Ambient Explorer',
		bpm: 80,
		key: 'Fm',
		genre: 'Ambient'
	}, {
		name: 'Drop The Beat',
		artist: 'Dubstep Master',
		bpm: 140,
		key: 'Gm',
		genre: 'Dubstep'
	}, {
		name: 'Trap Nation',
		artist: 'Trap Lord',
		bpm: 75,
		key: 'Cm',
		genre: 'Trap'
	}, {
		name: 'Liquid Motion',
		artist: 'DNB Producer',
		bpm: 175,
		key: 'Am',
		genre: 'Drum & Bass'
	}];
	self.categories = ['CATEGORY', 'All Tracks', 'House', 'Techno', 'EDM', 'Electronic', 'Disco', 'Latin', 'POP', 'Hip-Hop'];
	self.visibleTrackStart = 0;
	self.maxVisibleTracks = 8;
	// Create main container for app content
	var appContainer = self.addChild(new Container());
	// Header bar
	var headerBar = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 742,
		height: 50
	});
	headerBar.tint = 0x1a1a1a;
	appContainer.addChild(headerBar);
	// TRACKLIST logo/title
	var titleText = new Text2('TRACKLIST', {
		size: 56,
		fill: 0xFF6600
	});
	titleText.anchor.set(0, 0.5);
	titleText.x = 10;
	titleText.y = 25;
	appContainer.addChild(titleText);
	// Screen indicator
	var screenIndicator = new Text2('BROWSER', {
		size: 40,
		fill: 0xFFFFFF
	});
	screenIndicator.anchor.set(1, 0.5);
	screenIndicator.x = 722;
	screenIndicator.y = 25;
	appContainer.addChild(screenIndicator);
	// Sidebar for categories
	var sidebar = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 180,
		height: 400
	});
	sidebar.tint = 0x2a2a2a;
	sidebar.y = 60;
	appContainer.addChild(sidebar);
	// Main content area
	var contentArea = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 542,
		height: 400
	});
	contentArea.tint = 0x1a1a1a;
	contentArea.x = 190;
	contentArea.y = 60;
	appContainer.addChild(contentArea);
	// Category items in sidebar
	self.categoryItems = [];
	for (var i = 0; i < self.categories.length; i++) {
		var categoryBg = LK.getAsset('crossfaderTrack', {
			anchorX: 0,
			anchorY: 0,
			width: 170,
			height: 35
		});
		categoryBg.tint = i === self.selectedCategoryIndex ? 0x444444 : 0x333333;
		categoryBg.x = 5;
		categoryBg.y = 70 + i * 40;
		appContainer.addChild(categoryBg);
		var categoryText = new Text2(self.categories[i], {
			size: 28,
			fill: i === self.selectedCategoryIndex ? "#ff6600" : "#cccccc"
		});
		categoryText.anchor.set(0, 0.5);
		categoryText.x = 15;
		categoryText.y = 87 + i * 40 - 2 - 5;
		appContainer.addChild(categoryText);
		self.categoryItems.push({
			bg: categoryBg,
			text: categoryText
		});
		// Add touch handler to category item background
		categoryBg.down = function (x, y, obj) {
			var _this = this;
			// Find the index of this category item
			var index = self.categoryItems.findIndex(function (item) {
				return item.bg === _this;
			});
			if (index !== -1) {
				self.selectedCategoryIndex = index;
				self.selectedTrackIndex = 0; // Reset track selection when category changes
				self.visibleTrackStart = 0; // Reset visible tracks
				self.updateDisplay();
				// Visual feedback - scale animation
				tween(this, {
					scaleX: 1.05,
					scaleY: 1.05
				}, {
					duration: 80,
					onFinish: function () {
						tween(this, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 120
						});
					}.bind(this)
				});
				// Play sound effect
				LK.getSound('fx').play();
			}
		};
	}
	// Menubar graphic (added behind selected category)
	self.menubar = LK.getAsset('menubar', {
		anchorX: 0,
		anchorY: 0,
		width: 180,
		height: 40 //{nd.4}
	}); //{nd.5}
	self.menubar.alpha = 0.5; // Slightly transparent
	appContainer.addChild(self.menubar);
	// Ensure menubar is below category texts but above background
	appContainer.setChildIndex(self.menubar, appContainer.children.indexOf(self.categoryItems[0].bg)); // Position behind the first category background
	// Track list headers
	// Category texts will be initialized with proper fill colors in updateDisplay method
	var headerTrack = new Text2('TRACK', {
		size: 28,
		fill: 0x888888
	});
	headerTrack.anchor.set(0, 0);
	headerTrack.x = 200;
	headerTrack.y = 70;
	appContainer.addChild(headerTrack);
	var headerArtist = new Text2('ARTIST', {
		size: 28,
		fill: 0x888888
	});
	headerArtist.anchor.set(0, 0);
	headerArtist.x = 413; // Increased x-position
	headerArtist.y = 70;
	appContainer.addChild(headerArtist);
	var headerBPM = new Text2('BPM', {
		size: 28,
		fill: 0x888888
	});
	headerBPM.anchor.set(0, 0);
	headerBPM.x = 610; // Increased x-position and moved left by 10 units
	headerBPM.y = 70;
	appContainer.addChild(headerBPM);
	var headerKey = new Text2('KEY', {
		size: 28,
		fill: 0x888888
	});
	headerKey.anchor.set(0, 0);
	headerKey.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width
	headerKey.y = 70;
	appContainer.addChild(headerKey);
	// Track list items
	self.trackItems = [];
	for (var i = 0; i < self.maxVisibleTracks; i++) {
		var trackBg = LK.getAsset('crossfaderTrack', {
			anchorX: 0,
			anchorY: 0,
			width: 532,
			height: 35
		});
		trackBg.tint = 0x252525;
		trackBg.x = 195;
		trackBg.y = 95 + i * 40;
		var trackText = new Text2('', {
			size: 28,
			fill: 0xFFFFFF
		});
		trackText.anchor.set(0, 0.5);
		trackText.x = 200;
		trackText.y = 112 + i * 40 + 17 + 30;
		var artistText = new Text2('', {
			size: 28,
			//{ O}
			fill: 0xCCCCCC //{ P}
		}); //{ Q}
		artistText.anchor.set(0, 0.5);
		artistText.x = 380 + 25 + 10; // Increased x-position//{ O}
		artistText.y = 112 + i * 40 + 17 + 30;
		var bpmText = new Text2('', {
			size: 28,
			//{ R}
			fill: 0xAAAAAA //{ S}
		}); //{ T}
		bpmText.anchor.set(0, 0.5);
		bpmText.x = 620; // Increased x-position
		bpmText.y = 112 + i * 40 + 17 + 30;
		var keyText = new Text2('', {
			size: 28,
			//{ U}
			fill: 0xAAAAAA //{ V}
		}); //{ W}
		keyText.anchor.set(0, 0.5);
		keyText.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width
		keyText.y = 112 + i * 40 + 17 + 30;
		self.trackItems.push({
			bg: trackBg,
			track: trackText,
			artist: artistText,
			bpm: bpmText,
			key: keyText
		});
		// Store original tint
		self.trackItems[i].bg.originalTint = trackBg.tint;
		// Add touch handler to track item background
		trackBg.down = function (x, y, obj) {
			var _this2 = this;
			// Find the index of this visible track item
			var index = self.trackItems.findIndex(function (item) {
				return item.bg === _this2;
			});
			if (index !== -1) {
				self.selectedTrackIndex = self.visibleTrackStart + index;
				self.selectTrack(); // Select the track
				// Visual feedback - scale animation
				tween(this, {
					scaleX: 1.02,
					scaleY: 1.05
				}, {
					duration: 80,
					onFinish: function () {
						tween(this, {
							scaleX: 1.0,
							scaleY: 1.0
						}, {
							duration: 120
						});
					}.bind(this)
				});
			}
		};
	}
	// Add all backgrounds first to ensure they are at the back
	for (var i = 0; i < self.maxVisibleTracks; i++) {
		appContainer.addChild(self.trackItems[i].bg);
	}
	// Then add all text elements so they are rendered on top
	for (var i = 0; i < self.maxVisibleTracks; i++) {
		var item = self.trackItems[i];
		appContainer.addChild(item.track);
		appContainer.addChild(item.artist);
		appContainer.addChild(item.bpm);
		appContainer.addChild(item.key);
	}
	// Bottom status bar
	var statusBar = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 742,
		height: 30
	});
	statusBar.tint = 0x333333;
	statusBar.y = 470;
	appContainer.addChild(statusBar);
	var statusText = new Text2('Use arrow keys to navigate', {
		size: 24,
		fill: 0xCCCCCC
	});
	statusText.anchor.set(0, 0.5);
	statusText.x = 235;
	statusText.y = 485;
	appContainer.addChild(statusText);
	// Search bar background
	var searchBarBg = LK.getAsset('Searchbarbg', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 512,
		height: 40
	});
	searchBarBg.tint = 0xffffff;
	searchBarBg.x = 457 + 10; // Adjusted for center anchor and moved right by 12 units
	searchBarBg.y = 125; // Adjusted for center anchor
	self.addChild(searchBarBg); // Add directly to self instead of appContainer for independent positioning
	// Search bar text
	var searchText = new Text2('Search tracks...', {
		size: 28,
		fill: 0x888888
	});
	searchText.anchor.set(0.5, 0.5);
	searchText.x = 457; // Centered with search bar background and moved right by 12 units
	searchText.y = 125; // Centered with search bar background
	self.addChild(searchText); // Add directly to self instead of appContainer for independent positioning
	// Add touch handler to search text to show keyboard
	searchText.down = function (x, y, obj) {
		keyboardApp.visible = true;
		leftArrowButtonKeyboard.visible = true;
		rightArrowButtonKeyboard.visible = true;
		upArrowButtonKeyboard.visible = true;
		downArrowButtonKeyboard.visible = true;
		okButtonKeyboard.visible = true;
		// Bring keyboard and arrow buttons to the front
		game.addChild(keyboardApp);
		game.addChild(leftArrowButtonKeyboard);
		game.addChild(rightArrowButtonKeyboard);
		game.addChild(upArrowButtonKeyboard);
		game.addChild(downArrowButtonKeyboard);
		game.addChild(okButtonKeyboard);
	};
	// Track count info
	self.trackCountText = new Text2('', {
		size: 24,
		fill: 0x888888
	});
	self.trackCountText.anchor.set(1, 0.5);
	self.trackCountText.x = 732;
	self.trackCountText.y = 485;
	appContainer.addChild(self.trackCountText);
	// Methods
	self.updateDisplay = function () {
		// Update categories
		for (var i = 0; i < self.categoryItems.length; i++) {
			var isSelected = i === self.selectedCategoryIndex;
			self.categoryItems[i].bg.tint = isSelected ? 0x444444 : 0x333333;
			if (self.categoryItems[i].text) {
				self.categoryItems[i].text.fill = isSelected ? "#ff8000" : "#cccccc";
			}
		}
		// Position the menubar behind the selected category
		if (self.selectedCategoryIndex < self.categoryItems.length) {
			self.menubar.x = self.categoryItems[self.selectedCategoryIndex].bg.x;
			self.menubar.y = self.categoryItems[self.selectedCategoryIndex].bg.y;
			self.menubar.visible = true; // Make menubar visible
			// Ensure menubar is in appContainer before setting index
			if (appContainer.children.indexOf(self.menubar) === -1) {
				appContainer.addChild(self.menubar);
			}
			// Position behind the selected category background
			var targetIndex = appContainer.children.indexOf(self.categoryItems[self.selectedCategoryIndex].bg);
			if (targetIndex !== -1) {
				appContainer.setChildIndex(self.menubar, targetIndex);
			}
		} else {
			self.menubar.visible = false; // Hide menubar if no category is selected
		}
		// Filter tracks based on category
		var filteredTracks = self.tracks;
		if (self.selectedCategoryIndex > 1) {
			// Filter by specific genre, skipping 'CATEGORY' and 'All Tracks'
			var genre = self.categories[self.selectedCategoryIndex];
			filteredTracks = self.tracks.filter(function (track) {
				return track.genre === genre;
			});
		} else if (self.selectedCategoryIndex === 1) {
			// 'All Tracks' category - show all tracks
			filteredTracks = self.tracks;
		} else {
			// 'CATEGORY' category - show no tracks
			filteredTracks = [];
		}
		// Update track list
		for (var i = 0; i < self.maxVisibleTracks; i++) {
			var trackIndex = self.visibleTrackStart + i;
			var item = self.trackItems[i];
			if (trackIndex < filteredTracks.length) {
				var track = filteredTracks[trackIndex];
				var isSelected = trackIndex === self.selectedTrackIndex;
				item.bg.tint = isSelected ? 0xff8000 : i % 2 === 0 ? 0x252525 : 0x2a2a2a;
				item.track.setText(track.name.length > 20 ? track.name.substring(0, 17) + '...' : track.name);
				item.artist.setText(track.artist.length > 15 ? track.artist.substring(0, 12) + '...' : track.artist);
				item.bpm.setText(track.bpm.toString());
				item.key.setText(track.key);
				if (item.track) {
					item.track.fill = isSelected ? "#ff8000" : "#cccccc";
				}
				item.artist.fill = isSelected ? "#ffffff" : "#aaaaaa";
				item.bpm.fill = isSelected ? "#ffffff" : "#999999";
				item.key.fill = isSelected ? "#ffffff" : "#999999";
				item.bg.visible = true;
				item.track.visible = true;
				item.artist.visible = true;
				item.bpm.visible = true;
				item.key.visible = true;
				// Position the menubar behind the selected track item
				if (isSelected) {
					self.menubar.x = item.bg.x;
					self.menubar.y = item.bg.y;
					self.menubar.width = item.bg.width; // Match width to track item background
					self.menubar.height = item.bg.height; // Match height to track item background
					self.menubar.visible = true; // Make menubar visible
					// Ensure menubar is in appContainer before setting index
					if (appContainer.children.indexOf(self.menubar) === -1) {
						appContainer.addChild(self.menubar);
					}
					// Ensure menubar is behind track item visuals
					var targetIndex = appContainer.children.indexOf(item.bg);
					if (targetIndex !== -1) {
						appContainer.setChildIndex(self.menubar, targetIndex);
					}
				}
				item.artist.fill = isSelected ? "#ffffff" : "#aaaaaa";
				item.bpm.fill = isSelected ? "#ffffff" : "#999999";
				item.key.fill = isSelected ? "#ffffff" : "#999999";
				item.bg.visible = true;
				item.track.visible = true;
				item.artist.visible = true;
				item.bpm.visible = true;
				item.key.visible = true;
			} else {
				item.bg.visible = false;
				item.track.visible = false;
				item.artist.visible = false;
				item.bpm.visible = false;
				item.key.visible = false;
			}
		}
		// Update track count
		self.trackCountText.setText(filteredTracks.length + ' tracks');
		// Update screen indicator
		screenIndicator.setText(self.currentScreen.toUpperCase());
	};
	self.navigateUp = function () {
		if (self.selectedTrackIndex > 0) {
			self.selectedTrackIndex--;
			if (self.selectedTrackIndex < self.visibleTrackStart) {
				self.visibleTrackStart = self.selectedTrackIndex;
			}
			self.updateDisplay();
		}
	};
	self.navigateDown = function () {
		var maxTracks = self.tracks.length - 1;
		if (self.selectedTrackIndex < maxTracks) {
			self.selectedTrackIndex++;
			if (self.selectedTrackIndex >= self.visibleTrackStart + self.maxVisibleTracks) {
				self.visibleTrackStart = self.selectedTrackIndex - self.maxVisibleTracks + 1;
			}
			self.updateDisplay();
		}
	};
	self.navigateLeft = function () {
		if (self.selectedCategoryIndex > 0) {
			self.selectedCategoryIndex--;
			self.selectedTrackIndex = 0;
			self.visibleTrackStart = 0;
			self.updateDisplay();
		}
	};
	self.navigateRight = function () {
		if (self.selectedCategoryIndex < self.categories.length - 1) {
			self.selectedCategoryIndex++;
			self.selectedTrackIndex = 0;
			self.visibleTrackStart = 0;
			self.updateDisplay();
		}
	};
	self.selectTrack = function () {
		var filteredTracks = self.tracks;
		if (self.selectedCategoryIndex > 1) {
			// Filter by specific genre, skipping 'CATEGORY' and 'All Tracks'
			var genre = self.categories[self.selectedCategoryIndex];
			filteredTracks = self.tracks.filter(function (track) {
				return track.genre === genre;
			});
		} else if (self.selectedCategoryIndex === 1) {
			// 'All Tracks' category - show all tracks
			filteredTracks = self.tracks;
		} else {
			// 'CATEGORY' category - show no tracks
			filteredTracks = [];
		}
		if (self.selectedTrackIndex < filteredTracks.length) {
			var selectedTrack = filteredTracks[self.selectedTrackIndex];
			// Flash effect to show selection
			tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, {
				tint: 0x00ff00
			}, {
				duration: 150,
				onFinish: function onFinish() {
					tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, {
						tint: self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg.originalTint || (self.selectedTrackIndex - self.visibleTrackStart) % 2 === 0 ? 0x252525 : 0x2a2a2a
					}, {
						duration: 150
					});
				}
			});
			// Play the selected track (assuming track.name can be mapped to a sound asset ID)
			// Play the selected track (assuming track.name can be mapped to a sound asset ID)
			// Since we only have 'trackA' and 'trackB' assets, we'll cycle through them
			if (selectedTrack) {
				var trackToPlay = selectedTrack.name === 'Summer Vibes' ? 'trackA' : 'trackB';
				LK.playMusic(trackToPlay, {
					loop: true
				});
			} else {
				// Play a default sound or do nothing if no track is selected
				LK.getSound('fx').play();
			}
			// Add score for track selection
			if (typeof score !== 'undefined') {
				score += 25;
				combo += 1;
			}
		}
	};
	// Initialize display
	self.updateDisplay();
	return self;
});
// SmokeDiffuser: Animated smoke diffuser with rising smoke puffs
var SmokeDiffuser = Container.expand(function () {
	var self = Container.call(this);
	// Visuals - Diffuser base (small ellipse)
	var base = self.attachAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 1.0,
		width: 60,
		height: 30,
		y: 0
	});
	base.tint = 0x888888;
	base.alpha = 0.85;
	// Config
	var numParticles = 60; // Increased number of particles for denser smoke
	var particles = [];
	var particleMinSize = 48;
	var particleMaxSize = 96;
	var speedY = -3.5; // pixels per frame (upwards)
	var speedX = 0.7; // horizontal drift
	var particleLifetime = 2500; // ms // Increased particle lifetime
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t_color = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v;
				g = t_color;
				b = p;
				break;
			case 1:
				r = q;
				g = v;
				b = p;
				break;
			case 2:
				r = p;
				g = v;
				b = t_color;
				break;
			case 3:
				r = p;
				g = q;
				b = v;
				break;
			case 4:
				r = t_color;
				g = p;
				b = v;
				break;
			case 5:
				r = v;
				g = p;
				b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	self.createSmoke = function () {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		var puff = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size,
			height: size
		});
		// Smoke color depends on UV effect state
		if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
			// Colorful UV smoke
			var hue = Math.random();
			var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
			puff.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		} else {
			// Normal smoke color: light gray, some randomization
			var gray = 180 + Math.floor(Math.random() * 40);
			puff.tint = gray << 16 | gray << 8 | gray;
		}
		puff.alpha = 0.32 + Math.random() * 0.18;
		// Start at base, with slight random x offset
		puff.x = Math.random() * 16 - 8;
		puff.y = -base.height + Math.random() * 8;
		self.addChild(puff);
		particles.push({
			obj: puff,
			speedScale: 0.8 + Math.random() * 0.5,
			startTime: Date.now(),
			baseSize: size,
			phase: Math.random() * Math.PI * 2
		});
	};
	// Animation
	self.update = function () {
		var now = Date.now();
		// Only emit smoke if smokeEffectActive is true
		if (typeof smokeEffectActive !== 'undefined' && smokeEffectActive) {
			// Create new puffs if needed
			if (particles.length < numParticles) {
				self.createSmoke();
			}
		}
		// Update puffs
		for (var i = particles.length - 1; i >= 0; i--) {
			var p = particles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				particles.splice(i, 1);
			} else {
				// Move upwards, drift sideways
				p.obj.y += speedY * p.speedScale;
				p.obj.x += speedX * Math.sin(now * 0.001 + p.phase) * p.speedScale;
				// Fade out and grow
				p.obj.alpha = (1.0 - elapsed / particleLifetime) * (0.32 + 0.18 * Math.sin(now * 0.002 + p.phase));
				var size = p.baseSize * (1.0 + elapsed / particleLifetime * 0.7);
				p.obj.width = size;
				p.obj.height = size;
			}
		}
	};
	return self;
});
var SongMakerApp = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var appWidth = 2048;
	var appHeight = 2732;
	// Background
	var background = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: appWidth,
		height: appHeight
	});
	background.tint = 0x1a1a1a; // Dark background
	// App Title
	var titleText = new Text2('SONG MAKER', {
		size: 100,
		fill: 0xFF6600
	});
	titleText.anchor.set(0.5, 0);
	titleText.x = appWidth / 2;
	titleText.y = 100;
	self.addChild(titleText);
	// Piano Roll Configuration
	var pianoRollConfig = {
		x: 50,
		y: 250,
		width: appWidth - 100,
		height: 1600,
		noteHeight: 25,
		noteColors: [0xFF6600, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF],
		gridColor: 0x333333,
		keyboardWidth: 120,
		timelineHeight: 80,
		instruments: ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings']
	};
	// Create piano roll instance
	var pianoRoll = new PianoRoll(pianoRollConfig);
	pianoRoll.x = pianoRollConfig.x; // Position the container
	pianoRoll.y = pianoRollConfig.y; // Position the container
	self.addChild(pianoRoll);
	// Access notes array from the pianoRoll instance
	var notes = pianoRoll.notes; // This variable is now accessible
	var beatWidth = (pianoRollConfig.width - pianoRollConfig.keyboardWidth) / (16 * 4); // Recalculate beatWidth based on pianoRollConfig
	// Instruments tools container
	// Instruments tools container
	var instrumentsToolsY = pianoRollConfig.y + pianoRollConfig.height + 50;
	var instrumentsToolsHeight = 200; // Define height for instruments tools
	var instrumentsToolsContainer = self.addChild(new Container());
	instrumentsToolsContainer.x = pianoRollConfig.x;
	instrumentsToolsContainer.y = instrumentsToolsY;
	var instrumentsToolsBg = instrumentsToolsContainer.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: pianoRollConfig.width,
		height: instrumentsToolsHeight
	});
	instrumentsToolsBg.tint = 0x333333;
	instrumentsToolsBg.x = 0; // Relative to container
	instrumentsToolsBg.y = 0; // Relative to container
	// Transport controls
	var transportY = instrumentsToolsY + instrumentsToolsHeight + 50; // Position transport controls below instruments tools
	var transportBg = self.attachAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: pianoRollConfig.width,
		height: 100
	});
	transportBg.tint = 0x333333;
	transportBg.x = pianoRollConfig.x;
	transportBg.y = transportY;
	self.addChild(transportBg);
	// Play button
	var playButton = self.attachAsset('Startbutton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	playButton.x = pianoRollConfig.x + 100;
	playButton.y = transportY + 50;
	playButton.scaleX = 0.5;
	playButton.scaleY = 0.5;
	self.addChild(playButton);
	var isPlaying = false;
	var playbackPosition = 0;
	var playbackTimer = null;
	playButton.down = function (x, y, obj) {
		isPlaying = !isPlaying;
		if (isPlaying) {
			// Start playback
			playButton.tint = 0x00FF00;
			playbackTimer = LK.setInterval(function () {
				// Update playback position
				playbackPosition = (playbackPosition + 1) % (16 * 4); // Use totalBars * beatsPerBar
				// Play notes at current position
				for (var i = 0; i < notes.length; i++) {
					if (notes[i].beatIndex === playbackPosition) {
						// Play sound associated with the instrument voice
						// This needs a mapping from instrument voice to a sound asset ID
						// For now, playing a generic sound
						LK.getSound('fx').play();
						// Flash the note
						var originalTint = notes[i].visual.tint;
						notes[i].visual.tint = 0xFFFFFF;
						var noteRef = notes[i];
						LK.setTimeout(function () {
							noteRef.visual.tint = originalTint;
						}, 100);
					}
				}
			}, 250); // 240 BPM (250ms per beat)
		} else {
			// Stop playback
			playButton.tint = 0xFFFFFF;
			if (playbackTimer) {
				LK.clearInterval(playbackTimer);
				playbackTimer = null;
			}
		}
	};
	// Stop button
	var stopButton = self.attachAsset('Cuebutton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	stopButton.x = pianoRollConfig.x + 200;
	stopButton.y = transportY + 50;
	stopButton.scaleX = 0.5;
	stopButton.scaleY = 0.5;
	self.addChild(stopButton);
	stopButton.down = function (x, y, obj) {
		isPlaying = false;
		playbackPosition = 0;
		playButton.tint = 0xFFFFFF;
		if (playbackTimer) {
			LK.clearInterval(playbackTimer);
			playbackTimer = null;
		}
	};
	// Clear button
	var clearButton = self.attachAsset('Effect', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	clearButton.x = pianoRollConfig.x + 300;
	clearButton.y = transportY + 50;
	clearButton.scaleX = 0.8;
	clearButton.scaleY = 0.8;
	self.addChild(clearButton);
	var clearText = new Text2('CLEAR', {
		size: 24,
		fill: 0xFFFFFF
	});
	clearText.anchor.set(0.5, 0.5);
	clearText.x = clearButton.x;
	clearText.y = clearButton.y;
	self.addChild(clearText);
	clearButton.down = function (x, y, obj) {
		// Clear all notes
		for (var i = 0; i < notes.length; i++) {
			self.removeChild(notes[i].visual);
		}
		notes = []; // Clear the notes array
		// Visual feedback
		tween(clearButton, {
			scaleX: 0.6,
			scaleY: 0.6
		}, {
			duration: 100,
			onFinish: function onFinish() {
				tween(clearButton, {
					scaleX: 0.8,
					scaleY: 0.8
				}, {
					duration: 100
				});
			}
		});
	};
	// Tempo display
	var tempoText = new Text2('BPM: 240', {
		size: 32,
		fill: 0xFFFFFF
	});
	tempoText.anchor.set(0, 0.5);
	tempoText.x = pianoRollConfig.x + 450;
	tempoText.y = transportY + 50;
	self.addChild(tempoText);
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
var reflectors = [];
var numReflectors = 7;
var reflectorSpacing = 2048 / (numReflectors + 1);
var reflectorY = 80; // Top, but below menu area
// Create reflectors but do not add to game yet
for (var i = 0; i < numReflectors; i++) {
	var rx = reflectorSpacing * (i + 1);
	var reflector = LK.getAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60,
		height: 60
	});
	reflector.tint = 0xffffff;
	reflector.alpha = 0.0;
	reflector.x = rx;
	reflector.y = reflectorY;
	reflectors.push(reflector);
}
// --- Discoball (top of screen) ---
// Asset for dancing woman
// Asset for dancing man
var discoball = new Discoball();
// Add reflectors behind discoball in display order
for (var i = 0; i < reflectors.length; i++) {
	game.addChild(reflectors[i]);
}
// Insert discoball after reflectors so it appears above them
game.addChild(discoball);
discoball.x = 2048 / 2;
discoball.y = 200 - 69; // Move discoball up by 69 units
discoball.scaleX = 0.8;
discoball.scaleY = 0.8;
// --- Extra white particles for upper half sparkle effect ---
var extraWhiteParticles = [];
var numExtraParticles = 32;
for (var i = 0; i < numExtraParticles; i++) {
	// Random position in upper half, avoid top 100px (menu)
	var px = 100 + Math.random() * (2048 - 200);
	var py = 120 + Math.random() * (1366 - 220);
	var size = 8 + Math.random() * 10;
	var particle = LK.getAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: size,
		height: size
	});
	particle.tint = 0xffffff;
	particle.alpha = 0.18 + Math.random() * 0.22;
	particle.x = px;
	particle.y = py;
	game.addChild(particle);
	extraWhiteParticles.push({
		obj: particle,
		baseX: px,
		baseY: py,
		phase: Math.random() * Math.PI * 2,
		baseAlpha: particle.alpha,
		baseSize: size
	});
}
// Animate extra particles in game.update
// --- Fog Effect (upper half) ---
var fogEffect = new FogEffect();
game.addChild(fogEffect);
fogEffect.y = 0; // Position at the top of the screen
// --- Confetti Effect (upper half) ---
var confettiEffect = new ConfettiEffect();
game.addChild(confettiEffect);
confettiEffect.y = 0; // Position at the top of the screen
// --- DJDeck Asset (center of map) ---
var djDeck = LK.getAsset('desk', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 2048,
	height: 2020 * (2048 / 2040) // maintain aspect ratio
});
game.addChild(djDeck);
djDeck.x = 2048 / 2;
djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 units
// --- Rim Assets (both sides of djdeck) ---
var rimLeft = LK.getAsset('rim', {
	anchorX: 0.5,
	anchorY: 0.5
});
rimLeft.x = djDeck.x - djDeck.width / 2 + rimLeft.width / 2 + 49;
rimLeft.y = djDeck.y;
var rimRight = LK.getAsset('rim', {
	anchorX: 0.5,
	anchorY: 0.5
});
rimRight.x = djDeck.x + djDeck.width / 2 - rimRight.width / 2 - 150 + 108;
rimRight.y = djDeck.y;
// Add rim assets after djdeck to ensure they appear above in display order
game.addChild(rimLeft);
game.addChild(rimRight);
// Move rim assets above djdeck in display order
var djdeckIndex = game.children.indexOf(djDeck);
if (djdeckIndex !== -1) {
	game.setChildIndex(rimLeft, djdeckIndex + 1);
	game.setChildIndex(rimRight, djdeckIndex + 2);
}
// Remove and re-add rim assets to ensure they're above djdeckAsset in display order
if (game.children.indexOf(rimLeft) !== -1) {
	game.removeChild(rimLeft);
}
if (game.children.indexOf(rimRight) !== -1) {
	game.removeChild(rimRight);
}
game.addChild(rimLeft);
game.addChild(rimRight);
// --- Dancing People ---
var dancingPeople = [];
var numPeople = 8;
var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
var peopleAreaWidth = 1600 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer
var startX = 2048 / 2 - peopleAreaWidth / 2 - 77;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
	// Alternate: even index = woman, odd index = man
	var genderAsset = i % 2 === 0 ? 'dancingWoman' : 'dancingMan';
	var person = new DancingPerson(genderAsset);
	person.x = startX + i * personSpacing;
	person.baseY = peopleAreaY; // Set the base Y position for animation
	game.addChild(person);
	dancingPeople.push(person);
}
// --- Laser Show (upper half) ---
var laserShow = new LaserShow();
game.addChild(laserShow);
laserShow.y = -456; // Move laser effect up by 456 units (333 + 123)
// --- Fireworks Effect (upper half) ---
var fireworksEffect = new FireworksEffect();
game.addChild(fireworksEffect);
fireworksEffect.y = 0; // Position at the top of the screen
// --- Additional Fireworks Effects for more density in upper half ---
var fireworksEffect2 = new FireworksEffect();
game.addChild(fireworksEffect2);
fireworksEffect2.y = 0; // Position at the top of the screen
fireworksEffect2.x = 500; // Offset position for varied launch points
var fireworksEffect3 = new FireworksEffect();
game.addChild(fireworksEffect3);
fireworksEffect3.y = 0; // Position at the top of the screen
fireworksEffect3.x = -300; // Different offset position
var fireworksEffect4 = new FireworksEffect();
game.addChild(fireworksEffect4);
fireworksEffect4.y = 0; // Position at the top of the screen
fireworksEffect4.x = 800; // Another offset position
// --- Balloons (falling from top) ---
var balloons = [];
var numBalloons = 50; // Increased number of balloons
for (var i = 0; i < numBalloons; i++) {
	var balloon = new Balloon();
	// Position balloons initially above the screen
	balloon.y = -300 - Math.random() * 1000; // Start higher up for more variety
	balloon.x = Math.random() * 2048;
	game.addChild(balloon);
	balloons.push(balloon);
}
// --- Money Particles (falling from top) ---
var moneyParticles = [];
var numMoneyParticles = 25; // Number of money bills falling
for (var i = 0; i < numMoneyParticles; i++) {
	var money = new MoneyParticle();
	// Position money initially above the screen with staggered timing
	money.y = -100 - Math.random() * 800;
	money.x = Math.random() * 2048;
	game.addChild(money);
	moneyParticles.push(money);
	// Add initial sparkle effect with tween
	tween(money, {
		alpha: 0.7
	}, {
		duration: 1000 + Math.random() * 1000,
		easing: tween.easeInOut,
		onFinish: function () {
			tween(this, {
				alpha: 1.0
			}, {
				duration: 1000 + Math.random() * 1000,
				easing: tween.easeInOut
			});
		}.bind(money)
	});
}
// --- Ferris Wheel (upper half) ---
var ferrisWheel = new FerrisWheel();
game.addChild(ferrisWheel);
ferrisWheel.x = 2048 / 2 - 400;
ferrisWheel.y = 450; // Positioned in the upper-middle part of the screen
ferrisWheel.scaleX = 0.8;
ferrisWheel.scaleY = 0.8;
// --- DJ Deck Asset ---
var djDeck = LK.getAsset('desk', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 2048,
	height: 2020 * (2048 / 2040) // maintain aspect ratio
});
game.addChild(djDeck);
djDeck.x = 2048 / 2;
djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 units
// No dark overlay is present, so nothing to remove.
// --- Second Line Dancing People (append to existing array) ---
// Don't recreate the array, just append to existing dancingPeople array
var numPeople = 8;
var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
var peopleAreaWidth = 1600 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer
var startX = 2048 / 2 - peopleAreaWidth / 2;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
	var person = new DancingPerson(); // Use default random asset initially
	person.x = startX + i * personSpacing;
	person.baseY = peopleAreaY; // Set the base Y position for animation
	game.addChild(person);
	dancingPeople.push(person);
}
// --- Smoke Diffuser (left of people line) ---
// Add global smokeEffectActive variable to control smoke emission
var smokeEffectActive = false;
var fogAnimationActive = false;
var fogTimeout = null;
// --- Add 6 smoke diffusers spread out to the left of the people line, spaced 300 units apart, all moved right by 32 units ---
var smokeDiffusers = [];
for (var i = 0; i <= 5; i++) {
	var sd = new SmokeDiffuser();
	sd.x = startX - 80 + 32 + i * 300;
	sd.y = peopleAreaY + 18 - 50; // Align base with people feet, moved up by 50 units
	game.addChild(sd);
	smokeDiffusers.push(sd);
}
// --- Flamethrower ---
var flamethrower = new Flamethrower();
// Position the flamethrower to the right of the dancing people line
flamethrower.x = startX + peopleAreaWidth + 100 + 77 + 7; // Move flamethrower right by 84 units (77+7)
// Move the flamethrower down so the flame aligns with the top of the base, then up by 100 units
flamethrower.y = peopleAreaY + (flamethrower.height ? flamethrower.height * 0.5 : 75) - 100; // 75 is half of base height (150*0.5), fallback if height is not set
game.addChild(flamethrower);
// --- Second Flamethrower (left edge) ---
var flamethrowerLeft = new Flamethrower();
// Place at the left edge, aligned with the people line
flamethrowerLeft.x = startX - 200; // Move left flamethrower left by 100 more units
flamethrowerLeft.y = peopleAreaY + (flamethrowerLeft.height ? flamethrowerLeft.height * 0.5 : 75) - 100; // Move base up by 100 units
game.addChild(flamethrowerLeft);
// Ensure ferriswheel, cabins, and rim dots are always under the dancing people in display order
for (var i = 0; i < dancingPeople.length; i++) {
	// Find the minimum index among all dancing people
	var minIndex = game.children.length;
	for (var j = 0; j < dancingPeople.length; j++) {
		var idx = game.children.indexOf(dancingPeople[j]);
		if (idx !== -1 && idx < minIndex) minIndex = idx;
	}
	// Move ferrisWheel itself below all dancing people
	if (game.children.indexOf(ferrisWheel) > minIndex) {
		game.setChildIndex(ferrisWheel, minIndex);
	}
	// Move all cabins below all dancing people
	if (ferrisWheel.cabins) {
		for (var c = 0; c < ferrisWheel.cabins.length; c++) {
			var cabin = ferrisWheel.cabins[c];
			if (game.children.indexOf(cabin) > minIndex) {
				game.setChildIndex(cabin, minIndex);
			}
		}
	}
	// Move all rim dots (centerCircle assets attached to wheelAssembly) below all dancing people
	if (ferrisWheel.wheelAssembly && ferrisWheel.wheelAssembly.children) {
		for (var w = 0; w < ferrisWheel.wheelAssembly.children.length; w++) {
			var rimDot = ferrisWheel.wheelAssembly.children[w];
			// Only move rim dots (centerCircle assets, not hub or spokes)
			if (rimDot && rimDot.assetId === 'centerCircle') {
				// The rim dots are not direct children of game, but of wheelAssembly, so nothing to do here for game order
				// If in future, rim dots are added to game, ensure they are below dancing people
			}
		}
	}
	// Now, ensure this dancing person is above ferrisWheel and all its parts
	var maxFerrisIndex = game.children.indexOf(ferrisWheel);
	if (ferrisWheel.cabins) {
		for (var c = 0; c < ferrisWheel.cabins.length; c++) {
			var idx = game.children.indexOf(ferrisWheel.cabins[c]);
			if (idx > maxFerrisIndex) maxFerrisIndex = idx;
		}
	}
	// (Rim dots are not direct children of game, so not included)
	game.setChildIndex(dancingPeople[i], maxFerrisIndex + 1);
}
// Ensure balloons are behind the dancing people but in front of the DJ deck
var minDancingPeopleIndex = game.children.length;
for (var i = 0; i < dancingPeople.length; i++) {
	var idx = game.children.indexOf(dancingPeople[i]);
	if (idx !== -1 && idx < minDancingPeopleIndex) minDancingPeopleIndex = idx;
}
var djDeckIndex = game.children.indexOf(djdeckAsset); // Use djdeckAsset which is the visible asset
if (balloons) {
	for (var i = 0; i < balloons.length; i++) {
		var balloon = balloons[i];
		var balloonIndex = game.children.indexOf(balloon);
		if (balloonIndex !== -1) {
			// Position balloons just behind the dancing people (minIndex - 1)
			// But also ensure they are in front of the djdeckAsset (djDeckIndex + 1)
			var targetIndex = Math.max(djDeckIndex + 1, minDancingPeopleIndex - 1);
			// Make sure the targetIndex is valid
			if (targetIndex < game.children.length) {
				game.setChildIndex(balloon, targetIndex);
			} else {
				// If target index is out of bounds, place it just before the dancing people
				game.setChildIndex(balloon, minDancingPeopleIndex > 0 ? minDancingPeopleIndex - 1 : 0);
			}
		}
	}
}
// --- Decks ---
// Sound effects (dummy, as actual music is handled by LK)
// Beat lights
// FX Button
// Crossfader
// Deck platters (turntables)
// --- Layout constants ---
var deckY = 1366; // Move decks to vertical center of 2732px screen
var deckSpacing = 1000;
var crossfaderY = 2300;
var fxButtonY = 2200;
// --- Decks ---
var leftDeck = new DeckPlatter();
leftDeck.setTrack('A');
game.addChild(leftDeck);
leftDeck.x = 2048 / 2 - deckSpacing / 2;
leftDeck.y = deckY;
var rightDeck = new DeckPlatter();
rightDeck.setTrack('B');
game.addChild(rightDeck);
rightDeck.x = 2048 / 2 + deckSpacing / 2;
rightDeck.y = deckY;
// --- Crossfader ---
var crossfader = new Crossfader();
crossfader.x = 2048 / 2;
crossfader.y = crossfaderY + 333 - 124 + 100;
game.addChild(crossfader);
game.setChildIndex(crossfader, game.children.length - 1); // Ensure knob is on top
crossfader.setValue(0.5);
// --- FX Button ---
var fxButton = new FXButton();
game.addChild(fxButton);
fxButton.x = 2048 / 2 - 150;
fxButton.y = fxButtonY;
// --- Score / Combo / Energy ---
var energy = 50; // 0-100
var combo = 0;
var score = 0;
// Energy bar
var energyBarBg = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 600,
	height: 40
});
energyBarBg.tint = 0x222222;
energyBarBg.y = 0;
var energyBar = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 600,
	height: 40
});
energyBar.tint = 0x00ff00;
energyBar.y = 0;
var energyBarContainer = new Container();
energyBarContainer.addChild(energyBarBg);
energyBarContainer.addChild(energyBar);
energyBarContainer.x = 2048 / 2;
energyBarContainer.y = 200;
LK.gui.top.addChild(energyBarContainer);
// --- DJDeck Asset (center of map) ---
var djdeckAsset = LK.getAsset('djdeck', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(djdeckAsset);
djdeckAsset.x = 2048 / 2;
djdeckAsset.y = 2732 / 2 + 300 + 23 - 5 - 15 - 5;
// Scale djdeck to fit the screen width properly
djdeckAsset.width = 2048;
djdeckAsset.height = djdeckAsset.height * (2048 / djdeckAsset.width);
// Ensure rim assets are above djdeckAsset in display order
var djdeckAssetIndex = game.children.indexOf(djdeckAsset);
if (djdeckAssetIndex !== -1) {
	var rimLeftIndex = game.children.indexOf(rimLeft);
	var rimRightIndex = game.children.indexOf(rimRight);
	if (rimLeftIndex !== -1 && rimLeftIndex <= djdeckAssetIndex) {
		var targetIndexLeft = djdeckAssetIndex + 1;
		if (targetIndexLeft < game.children.length) {
			game.setChildIndex(rimLeft, targetIndexLeft);
		} else {
			// If target index is out of bounds, move to last position
			game.setChildIndex(rimLeft, game.children.length - 1);
		}
		// Update djdeckAssetIndex after moving rimLeft
		djdeckAssetIndex = game.children.indexOf(djdeckAsset);
	}
	if (rimRightIndex !== -1 && rimRightIndex <= djdeckAssetIndex) {
		// Get fresh index for rimLeft after potential move
		var currentRimLeftIndex = game.children.indexOf(rimLeft);
		var targetIndex = Math.max(djdeckAssetIndex + 1, currentRimLeftIndex + 1);
		if (targetIndex < game.children.length) {
			game.setChildIndex(rimRight, targetIndex);
		} else {
			// If target index is out of bounds, move to last position
			game.setChildIndex(rimRight, game.children.length - 1);
		}
	}
}
// --- Sampletable Asset (center of map) ---
var sampletableAsset = LK.getAsset('sampletable', {
	anchorX: 0.5,
	anchorY: 1.0
});
game.addChild(sampletableAsset);
sampletableAsset.x = 2048 / 2;
sampletableAsset.y = 2732;
// --- Crossfader ---
// Remove and re-add crossfader to ensure it's above the sampletable
if (game.children.indexOf(crossfader) !== -1) {
	game.removeChild(crossfader);
}
game.addChild(crossfader);
// --- FX Button ---
// Remove and re-add fxButton to ensure it's above the sampletable
if (game.children.indexOf(fxButton) !== -1) {
	game.removeChild(fxButton);
}
game.addChild(fxButton);
// --- Samplebutton Asset (left side corner of sampletable) ---
var samplebuttonAsset = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset);
samplebuttonAsset.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset.width / 2 + 50 - 4;
samplebuttonAsset.y = sampletableAsset.y - samplebuttonAsset.height / 2 - 50;
// Add functionality to first samplebutton
samplebuttonAsset.down = function (x, y, obj) {
	// Play sample1 sound
	LK.getSound('sample1').play();
	// Visual feedback - scale animation
	tween(samplebuttonAsset, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(samplebuttonAsset, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect
	LK.effects.flashObject(samplebuttonAsset, 0xff0000, 300);
};
// --- Second Samplebutton Asset (reusing existing asset for memory efficiency) ---
var samplebuttonAsset2 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset2);
samplebuttonAsset2.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset2.width / 2 + 200 - 4;
samplebuttonAsset2.y = sampletableAsset.y - samplebuttonAsset2.height / 2 - 50;
// --- Third Samplebutton Asset ---
var samplebuttonAsset3 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset3);
samplebuttonAsset3.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset3.width / 2 + 350;
samplebuttonAsset3.y = sampletableAsset.y - samplebuttonAsset3.height / 2 - 50;
// Add functionality to third samplebutton
samplebuttonAsset3.down = function (x, y, obj) {
	// Play sample3 sound
	LK.getSound('sample3').play();
	// Visual feedback - color flash animation
	tween(samplebuttonAsset3, {
		tint: 0x0000ff
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(samplebuttonAsset3, {
				tint: 0xffffff
			}, {
				duration: 150
			});
		}
	});
	// Flash effect with blue color
	LK.effects.flashObject(samplebuttonAsset3, 0x0000ff, 300);
	score += 50;
	combo += 1;
};
// --- Fourth Samplebutton Asset ---
var samplebuttonAsset4 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset4);
samplebuttonAsset4.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset4.width / 2 + 500 - 4;
samplebuttonAsset4.y = sampletableAsset.y - samplebuttonAsset4.height / 2 - 50;
// Add functionality to fourth samplebutton
samplebuttonAsset4.down = function (x, y, obj) {
	// Play sample4 sound
	LK.getSound('sample4').play();
	// Visual feedback - pulse animation
	tween(samplebuttonAsset4, {
		alpha: 0.3
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(samplebuttonAsset4, {
				alpha: 1.0
			}, {
				duration: 80
			});
		}
	});
	// Flash effect with purple color
	LK.effects.flashObject(samplebuttonAsset4, 0xff00ff, 300);
	score += 50;
	combo += 1;
};
// --- Second Line Samplebuttons (Above First Line) ---
// --- First Samplebutton Second Line ---
var samplebuttonAsset5 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset5);
samplebuttonAsset5.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset5.width / 2 + 50 - 4;
samplebuttonAsset5.y = sampletableAsset.y - samplebuttonAsset5.height / 2 - 250;
// Add functionality to fifth samplebutton
samplebuttonAsset5.down = function (x, y, obj) {
	// Play sample1 sound with different effect
	LK.getSound('sample1').play();
	// Visual feedback - scale animation
	tween(samplebuttonAsset5, {
		scaleX: 1.3,
		scaleY: 1.3
	}, {
		duration: 200,
		onFinish: function onFinish() {
			tween(samplebuttonAsset5, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 200
			});
		}
	});
	// Flash effect with cyan color
	LK.effects.flashObject(samplebuttonAsset5, 0x00ffff, 300);
	score += 75;
	combo += 1;
};
// --- Second Samplebutton Second Line ---
var samplebuttonAsset6 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset6);
samplebuttonAsset6.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset6.width / 2 + 200 - 4;
samplebuttonAsset6.y = sampletableAsset.y - samplebuttonAsset6.height / 2 - 250;
// Add functionality to sixth samplebutton
samplebuttonAsset6.down = function (x, y, obj) {
	// Play sample2 sound with different effect
	LK.getSound('sample2').play();
	// Visual feedback - scale animation
	tween(samplebuttonAsset6, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(samplebuttonAsset6, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 150
			});
		}
	});
	// Flash effect with magenta color
	LK.effects.flashObject(samplebuttonAsset6, 0xff00ff, 300);
	score += 75;
	combo += 1;
};
// --- Third Samplebutton Second Line ---
var samplebuttonAsset7 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset7);
samplebuttonAsset7.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset7.width / 2 + 350 - 4;
samplebuttonAsset7.y = sampletableAsset.y - samplebuttonAsset7.height / 2 - 250;
// Add functionality to seventh samplebutton
samplebuttonAsset7.down = function (x, y, obj) {
	// Play sample3 sound with different effect
	LK.getSound('sample3').play();
	// Visual feedback - pulsing tint animation
	tween(samplebuttonAsset7, {
		tint: 0xffff00,
		scaleX: 0.8,
		scaleY: 0.8
	}, {
		duration: 120,
		onFinish: function onFinish() {
			tween(samplebuttonAsset7, {
				tint: 0xffffff,
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 120
			});
		}
	});
	// Flash effect with yellow color
	LK.effects.flashObject(samplebuttonAsset7, 0xffff00, 300);
	score += 75;
	combo += 1;
};
// --- Fourth Samplebutton Second Line ---
var samplebuttonAsset8 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAsset8);
samplebuttonAsset8.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset8.width / 2 + 500 - 4;
samplebuttonAsset8.y = sampletableAsset.y - samplebuttonAsset8.height / 2 - 250;
// Add functionality to eighth samplebutton
samplebuttonAsset8.down = function (x, y, obj) {
	// Play sample4 sound with different effect
	LK.getSound('sample4').play();
	// Visual feedback - alpha fade
	tween(samplebuttonAsset8, {
		alpha: 0.5
	}, {
		duration: 300,
		onFinish: function onFinish() {
			tween(samplebuttonAsset8, {
				alpha: 1.0
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with orange color
	LK.effects.flashObject(samplebuttonAsset8, 0xff8000, 300);
	score += 75;
	combo += 1;
};
// Add different functionality to second samplebutton
samplebuttonAsset2.down = function (x, y, obj) {
	// Play sample2 sound (different from first button)
	LK.getSound('sample2').play();
	// Visual feedback - scale animation
	tween(samplebuttonAsset2, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(samplebuttonAsset2, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with different color
	LK.effects.flashObject(samplebuttonAsset2, 0x00ff00, 300);
	// Add score bonus for using sample buttons
	score += 50;
	combo += 1;
};
// --- Right Side Sample Buttons (First Line) ---
// --- First Samplebutton Right Side First Line ---
var samplebuttonAssetR1 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR1);
samplebuttonAssetR1.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR1.width / 2 - 50 + 4;
samplebuttonAssetR1.y = sampletableAsset.y - samplebuttonAssetR1.height / 2 - 50;
// Add functionality to first right side samplebutton
samplebuttonAssetR1.down = function (x, y, obj) {
	// Play sample1 sound
	LK.getSound('sample1').play();
	// Visual feedback - scale animation
	tween(samplebuttonAssetR1, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR1, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect
	LK.effects.flashObject(samplebuttonAssetR1, 0xff0000, 300);
};
// --- Second Samplebutton Right Side First Line ---
var samplebuttonAssetR2 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR2);
samplebuttonAssetR2.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR2.width / 2 - 200 + 4;
samplebuttonAssetR2.y = sampletableAsset.y - samplebuttonAssetR2.height / 2 - 50;
// Add functionality to second right side samplebutton
samplebuttonAssetR2.down = function (x, y, obj) {
	// Play sample2 sound
	LK.getSound('sample2').play();
	// Visual feedback - scale animation
	tween(samplebuttonAssetR2, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR2, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with different color
	LK.effects.flashObject(samplebuttonAssetR2, 0x00ff00, 300);
	// Add score bonus for using sample buttons
	score += 50;
	combo += 1;
};
// --- Third Samplebutton Right Side First Line ---
var samplebuttonAssetR3 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR3);
samplebuttonAssetR3.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR3.width / 2 - 350 + 4;
samplebuttonAssetR3.y = sampletableAsset.y - samplebuttonAssetR3.height / 2 - 50;
// Add functionality to third right side samplebutton
samplebuttonAssetR3.down = function (x, y, obj) {
	// Play sample3 sound
	LK.getSound('sample3').play();
	// Visual feedback - color flash animation
	tween(samplebuttonAssetR3, {
		tint: 0x0000ff
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR3, {
				tint: 0xffffff
			}, {
				duration: 150
			});
		}
	});
	// Flash effect with blue color
	LK.effects.flashObject(samplebuttonAssetR3, 0x0000ff, 300);
	score += 50;
	combo += 1;
};
// --- Left Deck Asset ---
var leftDeckAsset = LK.getAsset('Deck', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftDeckAsset);
leftDeckAsset.x = rimLeft.x;
leftDeckAsset.y = rimLeft.y;
// --- Right Deck Asset ---
var rightDeckAsset = LK.getAsset('Deck', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightDeckAsset);
rightDeckAsset.x = rimRight.x;
rightDeckAsset.y = rimRight.y;
// --- Right Side Sample Buttons (Second Line) ---
// --- First Samplebutton Right Side Second Line ---
var samplebuttonAssetR5 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR5);
samplebuttonAssetR5.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR5.width / 2 - 50 + 4;
samplebuttonAssetR5.y = sampletableAsset.y - samplebuttonAssetR5.height / 2 - 250;
// Add functionality to fifth right side samplebutton
samplebuttonAssetR5.down = function (x, y, obj) {
	// Play sample1 sound with different effect
	LK.getSound('sample1').play();
	// Visual feedback - scale animation
	tween(samplebuttonAssetR5, {
		scaleX: 1.3,
		scaleY: 1.3
	}, {
		duration: 200,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR5, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 200
			});
		}
	});
	// Flash effect with cyan color
	LK.effects.flashObject(samplebuttonAssetR5, 0x00ffff, 300);
	score += 75;
	combo += 1;
};
// --- Second Samplebutton Right Side Second Line ---
var samplebuttonAssetR6 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR6);
samplebuttonAssetR6.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR6.width / 2 - 200 + 4;
samplebuttonAssetR6.y = sampletableAsset.y - samplebuttonAssetR6.height / 2 - 250;
// Add functionality to sixth right side samplebutton
samplebuttonAssetR6.down = function (x, y, obj) {
	// Play sample2 sound with different effect
	LK.getSound('sample2').play();
	// Visual feedback - scale animation
	tween(samplebuttonAssetR6, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR6, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 150
			});
		}
	});
	// Flash effect with magenta color
	LK.effects.flashObject(samplebuttonAssetR6, 0xff00ff, 300);
	score += 75;
	combo += 1;
};
// --- Third Samplebutton Right Side Second Line ---
var samplebuttonAssetR7 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR7);
samplebuttonAssetR7.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR7.width / 2 - 350 + 4;
samplebuttonAssetR7.y = sampletableAsset.y - samplebuttonAssetR7.height / 2 - 250;
// Add functionality to seventh right side samplebutton
samplebuttonAssetR7.down = function (x, y, obj) {
	// Play sample3 sound with different effect
	LK.getSound('sample3').play();
	// Visual feedback - pulsing tint animation
	tween(samplebuttonAssetR7, {
		tint: 0xffff00,
		scaleX: 0.8,
		scaleY: 0.8
	}, {
		duration: 120,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR7, {
				tint: 0xffffff,
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 120
			});
		}
	});
	// Flash effect with yellow color
	LK.effects.flashObject(samplebuttonAssetR7, 0xffff00, 300);
	score += 75;
	combo += 1;
};
// --- Fourth Samplebutton Right Side Second Line ---
var samplebuttonAssetR8 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR8);
samplebuttonAssetR8.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR8.width / 2 - 500 + 4;
samplebuttonAssetR8.y = sampletableAsset.y - samplebuttonAssetR8.height / 2 - 250;
// --- MASTER Asset ---
var masterAsset = LK.getAsset('MASTER', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(masterAsset);
masterAsset.x = samplebuttonAssetR8.x - samplebuttonAssetR8.width / 2 - masterAsset.width / 2 - 50 - 100; // Position to the left of samplebuttonAssetR8, moved left by 50 units
masterAsset.y = samplebuttonAssetR8.y; // Align vertically with samplebuttonAssetR8
// Add logo asset between the fx and master button
var logoAsset = LK.getAsset('Logo', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(logoAsset);
logoAsset.x = crossfader.x;
logoAsset.y = crossfader.y + crossfader.height / 2 + 30; // Position under the crossfader
// Add white B letter under master button
var masterButtonBText = new Text2('B', {
	size: 48,
	fill: 0xFFFFFF // White color
});
masterButtonBText.anchor.set(0.5, 0.5);
masterButtonBText.x = masterAsset.x + 150;
masterButtonBText.y = masterAsset.y + masterAsset.height / 2 + 20; // Position under the button
game.addChild(masterButtonBText);
// Add functionality to master asset
masterAsset.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(masterAsset, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(masterAsset, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with white color
	LK.effects.flashObject(masterAsset, 0xffffff, 300);
	// Add score bonus
	score += 100;
	combo += 1;
};
// --- Fourth Samplebutton Right Side First Line ---
var samplebuttonAssetR4 = LK.getAsset('samplebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(samplebuttonAssetR4);
samplebuttonAssetR4.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR4.width / 2 - 500 + 4;
samplebuttonAssetR4.y = sampletableAsset.y - samplebuttonAssetR4.height / 2 - 50;
// Add functionality to fourth right side samplebutton
samplebuttonAssetR4.down = function (x, y, obj) {
	// Play sample4 sound
	LK.getSound('sample4').play();
	// Visual feedback - pulse animation
	tween(samplebuttonAssetR4, {
		alpha: 0.3
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR4, {
				alpha: 1.0
			}, {
				duration: 80
			});
		}
	});
	// Flash effect with purple color
	LK.effects.flashObject(samplebuttonAssetR4, 0xff00ff, 300);
	score += 50;
	combo += 1;
};
// Add functionality to eighth right side samplebutton
samplebuttonAssetR8.down = function (x, y, obj) {
	// Play sample4 sound with different effect
	LK.getSound('sample4').play();
	// Visual feedback - alpha fade
	tween(samplebuttonAssetR8, {
		alpha: 0.5
	}, {
		duration: 300,
		onFinish: function onFinish() {
			tween(samplebuttonAssetR8, {
				alpha: 1.0
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with orange color
	LK.effects.flashObject(samplebuttonAssetR8, 0xff8000, 300);
	score += 75;
	combo += 1;
};
// --- Digital Clock (center of map) ---
var digitalClockTxt = new Text2('', {
	size: 30,
	fill: "#fff"
});
digitalClockTxt.anchor.set(0.5, 0.5);
digitalClockTxt.x = 2048 / 2;
digitalClockTxt.y = 2732 / 2 - 500;
game.addChild(digitalClockTxt);
// Helper to format time as HH:MM:SS
function formatTime(date) {
	var h = date.getHours();
	var m = date.getMinutes();
	var s = date.getSeconds();
	function pad(n) {
		return n < 10 ? '0' + n : n;
	}
	return pad(h) + ':' + pad(m) + ':' + pad(s);
}
// Score text
var scoreTxt = new Text2('Score: 0', {
	size: 80,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 300;
// Combo text
var comboTxt = new Text2('', {
	size: 60,
	fill: "#ff0"
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.x = 2048 / 2;
comboTxt.y = 400;
// --- State ---
var dragging = null; // Which control is being dragged
var lastTouchObj = null;
// --- Beat simulation ---
var beatInterval = 600; // ms per beat
var beatTimer = 0;
var lastTickTime = Date.now();
// --- Music ---
LK.playMusic('trackA', {
	loop: true
});
LK.playMusic('trackB', {
	loop: true
});
// HSV to RGB helper for background color
function hsvToRgb(h, s, v) {
	var r, g, b;
	var i = Math.floor(h * 6);
	var f = h * 6 - i;
	var p = v * (1 - s);
	var q = v * (1 - f * s);
	var t = v * (1 - (1 - f) * s);
	switch (i % 6) {
		case 0:
			r = v, g = t, b = p;
			break;
		case 1:
			r = q, g = v, b = p;
			break;
		case 2:
			r = p, g = v, b = t;
			break;
		case 3:
			r = p, g = q, b = v;
			break;
		case 4:
			r = t, g = p, b = v;
			break;
		case 5:
			r = v, g = p, b = q;
			break;
	}
	return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
var trackAVol = 1;
var trackBVol = 1;
var trackAPlaying = false; // Track whether trackA is currently playing
// --- Touch handling ---
function getTouchedControl(x, y) {
	// Check decks
	var lx = leftDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).x;
	var ly = leftDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).y;
	if (Math.pow(lx - leftDeck.width / 2, 2) + Math.pow(ly - leftDeck.height / 2, 2) < 200 * 200) return leftDeck;
	var rx = rightDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).x;
	var ry = rightDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).y;
	if (Math.pow(rx - rightDeck.width / 2, 2) + Math.pow(ry - rightDeck.height / 2, 2) < 200 * 200) return rightDeck;
	// Crossfader
	var cf = crossfader.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (cf.x > 0 && cf.x < crossfader.width && cf.y > 0 && cf.y < crossfader.height) return crossfader;
	// FX Button
	var fx = fxButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(fx.x - fxButton.width / 2, 2) + Math.pow(fx.y - fxButton.height / 2, 2) < 60 * 60) return fxButton;
	// Check first samplebutton
	var sb1 = samplebuttonAsset.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb1.x - samplebuttonAsset.width / 2, 2) + Math.pow(sb1.y - samplebuttonAsset.height / 2, 2) < 75 * 75) return samplebuttonAsset;
	// Check second samplebutton
	var sb2 = samplebuttonAsset2.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb2.x - samplebuttonAsset2.width / 2, 2) + Math.pow(sb2.y - samplebuttonAsset2.height / 2, 2) < 75 * 75) return samplebuttonAsset2;
	// Check third samplebutton
	var sb3 = samplebuttonAsset3.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb3.x - samplebuttonAsset3.width / 2, 2) + Math.pow(sb3.y - samplebuttonAsset3.height / 2, 2) < 75 * 75) return samplebuttonAsset3;
	// Check fourth samplebutton
	var sb4 = samplebuttonAsset4.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb4.x - samplebuttonAsset4.width / 2, 2) + Math.pow(sb4.y - samplebuttonAsset4.height / 2, 2) < 75 * 75) return samplebuttonAsset4;
	// Check second line sample buttons
	// Check fifth samplebutton (second line, first button)
	var sb5 = samplebuttonAsset5.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb5.x - samplebuttonAsset5.width / 2, 2) + Math.pow(sb5.y - samplebuttonAsset5.height / 2, 2) < 75 * 75) return samplebuttonAsset5;
	// Check sixth samplebutton (second line, second button)
	var sb6 = samplebuttonAsset6.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb6.x - samplebuttonAsset6.width / 2, 2) + Math.pow(sb6.y - samplebuttonAsset6.height / 2, 2) < 75 * 75) return samplebuttonAsset6;
	// Check seventh samplebutton (second line, third button)
	var sb7 = samplebuttonAsset7.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb7.x - samplebuttonAsset7.width / 2, 2) + Math.pow(sb7.y - samplebuttonAsset7.height / 2, 2) < 75 * 75) return samplebuttonAsset7;
	// Check eighth samplebutton (second line, fourth button)
	var sb8 = samplebuttonAsset8.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sb8.x - samplebuttonAsset8.width / 2, 2) + Math.pow(sb8.y - samplebuttonAsset8.height / 2, 2) < 75 * 75) return samplebuttonAsset8;
	// Check right side sample buttons
	// Check first right side samplebutton (first line)
	var sbR1 = samplebuttonAssetR1.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR1.x - samplebuttonAssetR1.width / 2, 2) + Math.pow(sbR1.y - samplebuttonAssetR1.height / 2, 2) < 75 * 75) return samplebuttonAssetR1;
	// Check second right side samplebutton (first line)
	var sbR2 = samplebuttonAssetR2.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR2.x - samplebuttonAssetR2.width / 2, 2) + Math.pow(sbR2.y - samplebuttonAssetR2.height / 2, 2) < 75 * 75) return samplebuttonAssetR2;
	// Check third right side samplebutton (first line)
	var sbR3 = samplebuttonAssetR3.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR3.x - samplebuttonAssetR3.width / 2, 2) + Math.pow(sbR3.y - samplebuttonAssetR3.height / 2, 2) < 75 * 75) return samplebuttonAssetR3;
	// Check fourth right side samplebutton (first line)
	var sbR4 = samplebuttonAssetR4.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR4.x - samplebuttonAssetR4.width / 2, 2) + Math.pow(sbR4.y - samplebuttonAssetR4.height / 2, 2) < 75 * 75) return samplebuttonAssetR4;
	// Check fifth right side samplebutton (second line)
	var sbR5 = samplebuttonAssetR5.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR5.x - samplebuttonAssetR5.width / 2, 2) + Math.pow(sbR5.y - samplebuttonAssetR5.height / 2, 2) < 75 * 75) return samplebuttonAssetR5;
	// Check sixth right side samplebutton (second line)
	var sbR6 = samplebuttonAssetR6.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR6.x - samplebuttonAssetR6.width / 2, 2) + Math.pow(sbR6.y - samplebuttonAssetR6.height / 2, 2) < 75 * 75) return samplebuttonAssetR6;
	// Check seventh right side samplebutton (second line)
	var sbR7 = samplebuttonAssetR7.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR7.x - samplebuttonAssetR7.width / 2, 2) + Math.pow(sbR7.y - samplebuttonAssetR7.height / 2, 2) < 75 * 75) return samplebuttonAssetR7;
	// Check eighth right side samplebutton (second line)
	var sbR8 = samplebuttonAssetR8.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(sbR8.x - samplebuttonAssetR8.width / 2, 2) + Math.pow(sbR8.y - samplebuttonAssetR8.height / 2, 2) < 75 * 75) return samplebuttonAssetR8;
	// Check right loop exit button
	var rightLoopExitLocal = rightLoopExitButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rightLoopExitLocal.x - rightLoopExitButton.width / 2, 2) + Math.pow(rightLoopExitLocal.y - rightLoopExitButton.height / 2, 2) < 50 * 50) return rightLoopExitButton;
	// Check right in button
	var rightInLocal = rightInButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rightInLocal.x - rightInButton.width / 2, 2) + Math.pow(rightInLocal.y - rightInButton.height / 2, 2) < 50 * 50) return rightInButton;
	// Check right out button
	var rightOutLocal = rightOutButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rightOutLocal.x - rightOutButton.width / 2, 2) + Math.pow(rightOutLocal.y - rightOutButton.height / 2, 2) < 50 * 50) return rightOutButton;
	// Check left start button
	var leftStartLocal = leftStartButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(leftStartLocal.x - leftStartButton.width / 2, 2) + Math.pow(leftStartLocal.y - leftStartButton.height / 2, 2) < 100 * 100) return leftStartButton;
	// Check right start button
	var rightStartLocal = rightStartButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rightStartLocal.x - rightStartButton.width / 2, 2) + Math.pow(rightStartLocal.y - rightStartButton.height / 2, 2) < 100 * 100) return rightStartButton;
	// Check left cue button
	var leftCueLocal = leftCueButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(leftCueLocal.x - leftCueButton.width / 2, 2) + Math.pow(leftCueLocal.y - leftCueButton.height / 2, 2) < 50 * 50) return leftCueButton;
	// Check right cue button
	var rightCueLocal = rightCueButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rightCueLocal.x - rightCueButton.width / 2, 2) + Math.pow(rightCueLocal.y - rightCueButton.height / 2, 2) < 50 * 50) return rightCueButton;
	// Check vertical crossfader track
	var verticalCfLocal = verticalCrossfaderTrack.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (verticalCfLocal.x > 0 && verticalCfLocal.x < verticalCrossfaderTrack.width && verticalCfLocal.y > 0 && verticalCfLocal.y < verticalCrossfaderTrack.height) return verticalCrossfaderTrack;
	// Check second vertical crossfader track
	var verticalCfLocal2 = verticalCrossfaderTrack2.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (verticalCfLocal2.x > 0 && verticalCfLocal2.x < verticalCrossfaderTrack2.width && verticalCfLocal2.y > 0 && verticalCfLocal2.y < verticalCrossfaderTrack2.height) return verticalCrossfaderTrack2;
	// Check filter knobs
	var lfh = leftFilterKnobHigh.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(lfh.x - leftFilterKnobHigh.width / 2, 2) + Math.pow(lfh.y - leftFilterKnobHigh.height / 2, 2) < 60 * 60) return leftFilterKnobHigh;
	var lfm = leftFilterKnobMid.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(lfm.x - leftFilterKnobMid.width / 2, 2) + Math.pow(lfm.y - leftFilterKnobMid.height / 2, 2) < 60 * 60) return leftFilterKnobMid;
	var lfl = leftFilterKnobLow.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(lfl.x - leftFilterKnobLow.width / 2, 2) + Math.pow(lfl.y - leftFilterKnobLow.height / 2, 2) < 60 * 60) return leftFilterKnobLow;
	var rfh = rightFilterKnobHigh.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rfh.x - rightFilterKnobHigh.width / 2, 2) + Math.pow(rfh.y - rightFilterKnobHigh.height / 2, 2) < 60 * 60) return rightFilterKnobHigh;
	var rfm = rightFilterKnobMid.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rfm.x - rightFilterKnobMid.width / 2, 2) + Math.pow(rfm.y - rightFilterKnobMid.height / 2, 2) < 60 * 60) return rightFilterKnobMid;
	var rfl = rightFilterKnobLow.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(rfl.x - rightFilterKnobLow.width / 2, 2) + Math.pow(rfl.y - rightFilterKnobLow.height / 2, 2) < 60 * 60) return rightFilterKnobLow;
}
game.down = function (x, y, obj) {
	var control = getTouchedControl(x, y);
	dragging = control;
	lastTouchObj = obj;
	if (control && control.down) {
		// Convert to local
		var local = control.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		control.down(local.x, local.y, obj);
	}
	lastTouchObj = obj; // Keep track of the object that was touched down
};
game.up = function (x, y, obj) {
	if (dragging && dragging.up) {
		var local = dragging.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		dragging.up(local.x, local.y, obj);
	}
	// Reset filter knob drag state for vertical drag mode
	if (dragging === leftFilterKnobHigh || dragging === leftFilterKnobMid || dragging === leftFilterKnobLow || dragging === rightFilterKnobHigh || dragging === rightFilterKnobMid || dragging === rightFilterKnobLow) {
		if (typeof dragging._lastY !== "undefined") delete dragging._lastY;
		if (typeof dragging._rotationValue !== "undefined") delete dragging._rotationValue;
	}
	// Only set dragging to null if the released object was the one being dragged
	if (dragging === getTouchedControl(x, y) || !getTouchedControl(x, y)) {
		// Check if released on the same object or empty space
		dragging = null;
	}
	lastTouchObj = null;
};
game.move = function (x, y, obj) {
	if (dragging) {
		var local = dragging.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		if (dragging === verticalCrossfaderTrack) {
			// Use the object's own move method for consistent behavior
			dragging.move(local.x, local.y, obj);
		} else if (dragging === verticalCrossfaderTrack2) {
			// Use the object's own move method for consistent behavior
			dragging.move(local.x, local.y, obj);
		} else if (dragging === leftFilterKnobHigh || dragging === leftFilterKnobMid || dragging === leftFilterKnobLow || dragging === rightFilterKnobHigh || dragging === rightFilterKnobMid || dragging === rightFilterKnobLow) {
			// --- Filter knob rotation logic: vertical drag (up = right, down = left) ---
			// Store lastY on the knob for delta calculation
			if (typeof dragging._lastY === "undefined") {
				dragging._lastY = local.y;
				dragging._rotationValue = dragging.rotation || 0; // Store current rotation in radians
			}
			// Calculate vertical movement delta
			var deltaY = local.y - dragging._lastY;
			// Sensitivity: how much vertical movement for full rotation (in px)
			var sensitivity = 120; // 120px drag = full range (-150deg to +150deg)
			// Calculate rotation change: up = right (increase), down = left (decrease)
			var deltaRotation = -deltaY * (Math.PI * 5 / 6) / sensitivity; // 5/6 PI = 150deg
			// Clamp rotation to -150deg to +150deg
			var minRotation = -Math.PI * 5 / 6;
			var maxRotation = Math.PI * 5 / 6;
			dragging._rotationValue = Math.max(minRotation, Math.min(maxRotation, (dragging._rotationValue || 0) + deltaRotation));
			dragging.rotation = dragging._rotationValue;
			// Update lastY for next move
			dragging._lastY = local.y;
		} else if (dragging.move) {
			// Handle move for other objects that have a move method
			dragging.move(local.x, local.y, obj);
		}
	}
};
// --- Start Button State Variables ---
var leftStartButtonOn = false;
var rightStartButtonOn = false;
// --- Deck Spinning State Variables ---
var leftDeckSpinning = false;
var rightDeckSpinning = false;
// --- Left Start Button ---
var leftStartButton = LK.getAsset('Startbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftStartButton);
leftStartButton.x = rimLeft.x - 200;
leftStartButton.y = rimLeft.y + rimLeft.height / 2 + leftStartButton.height / 2 + 20;
// --- Left Cue Button ---
var leftCueButton = LK.getAsset('Cuebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftCueButton);
leftCueButton.x = leftStartButton.x + leftStartButton.width / 2 + leftCueButton.width / 2 + 20 + 200 - 20;
leftCueButton.y = leftStartButton.y;
// Add functionality to left start button
leftStartButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(leftStartButton, {
		scaleX: 1.3,
		scaleY: 1.3
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(leftStartButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 150
			});
		}
	});
	// Toggle button state
	leftStartButtonOn = !leftStartButtonOn;
	// Stop any existing tween first
	tween.stop(leftStartButton, {
		tint: true
	});
	if (leftStartButtonOn) {
		// Turn on green light effect
		tween(leftStartButton, {
			tint: 0x00ff00
		}, {
			duration: 200,
			onFinish: function onFinish() {
				// Create pulsing green light effect
				function createPulse() {
					if (leftStartButtonOn) {
						// Only continue pulsing if still on
						tween(leftStartButton, {
							tint: 0x88ff88
						}, {
							duration: 800,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								if (leftStartButtonOn) {
									// Only continue pulsing if still on
									tween(leftStartButton, {
										tint: 0x00ff00
									}, {
										duration: 800,
										easing: tween.easeInOut,
										onFinish: createPulse
									});
								}
							}
						});
					}
				}
				createPulse();
			}
		});
	} else {
		// Turn off light effect - return to normal tint
		tween(leftStartButton, {
			tint: 0xffffff
		}, {
			duration: 200
		});
	}
	// Start/stop left deck
	leftDeck.playing = !leftDeck.playing;
	// Start/stop left deck spinning
	leftDeckSpinning = !leftDeckSpinning;
	if (leftDeckSpinning) {
		// Start continuous clockwise spinning synchronized with deckplatter
		var _spinLeftDeck = function spinLeftDeck() {
			if (leftDeckSpinning) {
				// Synchronize deck asset rotation with deckplatter rotation speed
				// Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds)
				// To match this, deck asset should complete rotation in same time
				tween(leftDeckAsset, {
					rotation: leftDeckAsset.rotation + Math.PI * 2
				}, {
					duration: 5233,
					easing: tween.linear,
					onFinish: _spinLeftDeck
				});
			}
		};
		_spinLeftDeck();
	} else {
		// Stop spinning by stopping any rotation tweens
		tween.stop(leftDeckAsset, {
			rotation: true
		});
	}
	// Play beat sound
	LK.getSound('beat').play();
	// Add score bonus
	score += 100;
	combo += 1;
};
// --- Right Start Button ---
var rightStartButton = LK.getAsset('Startbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightStartButton);
rightStartButton.x = rimRight.x - 200;
rightStartButton.y = rimRight.y + rimRight.height / 2 + rightStartButton.height / 2 + 20;
// --- Right Cue Button ---
var rightCueButton = LK.getAsset('Cuebutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightCueButton);
rightCueButton.x = rightStartButton.x + rightStartButton.width / 2 + rightCueButton.width / 2 + 20 + 200 - 20 - 5 - 5;
rightCueButton.y = rightStartButton.y;
// Add functionality to right start button
rightStartButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(rightStartButton, {
		scaleX: 1.3,
		scaleY: 1.3
	}, {
		duration: 150,
		onFinish: function onFinish() {
			tween(rightStartButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 150
			});
		}
	});
	// Toggle button state
	rightStartButtonOn = !rightStartButtonOn;
	// Stop any existing tween first
	tween.stop(rightStartButton, {
		tint: true
	});
	if (rightStartButtonOn) {
		// Turn on green light effect
		tween(rightStartButton, {
			tint: 0x00ff00
		}, {
			duration: 200,
			onFinish: function onFinish() {
				// Create pulsing green light effect
				function createPulse() {
					if (rightStartButtonOn) {
						// Only continue pulsing if still on
						tween(rightStartButton, {
							tint: 0x88ff88
						}, {
							duration: 800,
							easing: tween.easeInOut,
							onFinish: function onFinish() {
								if (rightStartButtonOn) {
									// Only continue pulsing if still on
									tween(rightStartButton, {
										tint: 0x00ff00
									}, {
										duration: 800,
										easing: tween.easeInOut,
										onFinish: createPulse
									});
								}
							}
						});
					}
				}
				createPulse();
			}
		});
	} else {
		// Turn off light effect - return to normal tint
		tween(rightStartButton, {
			tint: 0xffffff
		}, {
			duration: 200
		});
	}
	// Start/stop right deck
	rightDeck.playing = !rightDeck.playing;
	// Start/stop right deck spinning
	rightDeckSpinning = !rightDeckSpinning;
	if (rightDeckSpinning) {
		// Start continuous clockwise spinning synchronized with deckplatter
		var _spinRightDeck = function spinRightDeck() {
			if (rightDeckSpinning) {
				// Synchronize deck asset rotation with deckplatter rotation speed
				// Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds)
				// To match this, deck asset should complete rotation in same time
				tween(rightDeckAsset, {
					rotation: rightDeckAsset.rotation + Math.PI * 2
				}, {
					duration: 5233,
					easing: tween.linear,
					onFinish: _spinRightDeck
				});
			}
		};
		_spinRightDeck();
	} else {
		// Stop spinning by stopping any rotation tweens
		tween.stop(rightDeckAsset, {
			rotation: true
		});
	}
	// Play beat sound
	LK.getSound('beat').play();
	// Add score bonus
	score += 100;
	combo += 1;
};
// Add functionality to left cue button
leftCueButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(leftCueButton, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(leftCueButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with cyan color
	LK.effects.flashObject(leftCueButton, 0x00ffff, 300);
	// Reset left deck to start position
	leftDeck.rotationOffset = 0;
	leftDeck.currentRotation = 0;
	leftDeck.rotation = 0;
	// Play scratch sound
	LK.getSound('scratch').play();
	// Add score bonus
	score += 50;
	combo += 1;
};
// Add functionality to right cue button
rightCueButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(rightCueButton, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(rightCueButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with magenta color
	LK.effects.flashObject(rightCueButton, 0xff00ff, 300);
	// Reset right deck to start position
	rightDeck.rotationOffset = 0;
	rightDeck.currentRotation = 0;
	rightDeck.rotation = 0;
	// Play scratch sound
	LK.getSound('scratch').play();
	// Add score bonus
	score += 50;
	combo += 1;
};
// Center left deck platter to the left deck center
leftDeck.x = leftDeckAsset.x;
leftDeck.y = leftDeckAsset.y - 3;
// Center right deck platter to the right deck center
rightDeck.x = rightDeckAsset.x;
rightDeck.y = rightDeckAsset.y - 3;
// --- Screens above decks ---
// Left deck screen
var leftDeckScreen = LK.getAsset('screen', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftDeckScreen);
leftDeckScreen.x = leftDeckAsset.x;
leftDeckScreen.y = leftDeckAsset.y - leftDeckAsset.height / 2 - leftDeckScreen.height / 2 - 50 - 200;
// Right deck screen
var rightDeckScreen = LK.getAsset('screen', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightDeckScreen);
rightDeckScreen.x = rightDeckAsset.x;
rightDeckScreen.y = rightDeckAsset.y - rightDeckAsset.height / 2 - rightDeckScreen.height / 2 - 50 - 200;
// --- Effect Buttons Above Screens ---
var effectButtons = [];
var effectButtonSpacing = 220;
var effectButtonY = leftDeckScreen.y - leftDeckScreen.height / 2 - 80 - 55;
// Create 6 effect buttons (3 above each screen)
for (var i = 0; i < 6; i++) {
	var effectButton = LK.getAsset('Effect', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	game.addChild(effectButton);
	// Position 3 buttons above left screen, 3 above right screen
	if (i < 3) {
		// Left side buttons
		effectButton.x = leftDeckScreen.x - effectButtonSpacing + i * effectButtonSpacing;
	} else {
		// Right side buttons
		effectButton.x = rightDeckScreen.x - effectButtonSpacing + (i - 3) * effectButtonSpacing;
	}
	effectButton.y = effectButtonY;
	// Add 'FIRE' text above the first effect button (i === 0)
	if (i === 0) {
		var fireText = new Text2('FIRE', {
			size: 36,
			fill: 0xFFFFFF
		});
		fireText.anchor.set(0.5, 0.5);
		fireText.x = effectButton.x;
		fireText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(fireText);
	}
	// Add 'LASER' text above the second effect button (i === 1)
	if (i === 1) {
		var laserText = new Text2('LASER', {
			size: 36,
			fill: 0xFFFFFF
		});
		laserText.anchor.set(0.5, 0.5);
		laserText.x = effectButton.x;
		laserText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(laserText);
	}
	// Add 'LIGHT' text above the third effect button (i === 2)
	if (i === 2) {
		var lightText = new Text2('LIGHT', {
			size: 36,
			fill: 0xFFFFFF
		});
		lightText.anchor.set(0.5, 0.5);
		lightText.x = effectButton.x;
		lightText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(lightText);
		// Store the third effect button for later access
		effectButtons[i] = effectButton;
	}
	// Add 'SMOKE' text above the fourth effect button (i === 3)
	if (i === 3) {
		var smokeText = new Text2('SMOKE', {
			size: 36,
			fill: 0xFFFFFF
		});
		smokeText.anchor.set(0.5, 0.5);
		smokeText.x = effectButton.x;
		smokeText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(smokeText);
	}
	// Add 'UV' text above the fifth effect button (i === 4)
	if (i === 4) {
		var uvText = new Text2('UV', {
			size: 36,
			fill: 0xFFFFFF
		});
		uvText.anchor.set(0.5, 0.5);
		uvText.x = effectButton.x;
		uvText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(uvText);
	}
	// Add 'GIFTS' text above the sixth effect button (i === 5)
	if (i === 5) {
		var giftsText = new Text2('GIFTS', {
			size: 36,
			fill: 0xFFFFFF
		});
		giftsText.anchor.set(0.5, 0.5);
		giftsText.x = effectButton.x;
		giftsText.y = effectButton.y - effectButton.height / 2 - 80;
		game.addChild(giftsText);
	}
	// Scale up effect button by 1.5
	tween(effectButton, {
		scaleX: 1.5,
		scaleY: 1.5
	}, {
		duration: 300,
		easing: tween.easeOut
	});
	// Add up handler for SMOKE button (index 3)
	if (i === 3) {
		effectButton.down = function (x, y, obj) {
			smokeEffectActive = true;
			fogAnimationActive = true;
			// Clear any existing fog timeout
			if (fogTimeout) {
				LK.clearTimeout(fogTimeout);
				fogTimeout = null;
			}
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			LK.getSound('fx').play();
			LK.effects.flashObject(this, 0x888888, 200);
		};
		effectButton.up = function (x, y, obj) {
			smokeEffectActive = false;
			// Start 4 second countdown to turn off fog animation
			fogTimeout = LK.setTimeout(function () {
				fogAnimationActive = false;
				fogTimeout = null;
			}, 4000);
			// Optionally, provide a quick scale feedback on release
			tween(this, {
				scaleX: 1.5,
				scaleY: 1.5
			}, {
				duration: 100
			});
		};
	} else {
		// Add functionality to other effect buttons
		effectButton.down = function (x, y, obj) {
			// Visual feedback - scale animation that returns to 1.5 instead of 1.0
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Flash effect with random color
			var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
			var randomColor = colors[Math.floor(Math.random() * colors.length)];
			LK.effects.flashObject(this, randomColor, 400);
			// Add score bonus
			score += 75;
			combo += 1;
		};
	}
	// If this is the first effect button (index 0) - FIRE button
	if (i === 0) {
		effectButton.down = function (x, y, obj) {
			// Visual feedback - scale animation that returns to 1.5 instead of 1.0
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Cycle through fire effect modes: 0 (off) -> 1 (normal) -> 2 (big) -> 0 (off)
			fireEffectMode = (fireEffectMode + 1) % 3;
			// Flash effect with different colors based on mode
			var fireColors;
			if (fireEffectMode === 0) {
				// Off mode - gray color
				fireColors = [0x888888];
			} else if (fireEffectMode === 1) {
				// Normal mode - orange colors
				fireColors = [0xff8000, 0xff4400, 0xffaa00];
			} else {
				// Big flames mode - red colors
				fireColors = [0xff0000, 0xff4444, 0xff8888];
			}
			var randomColor = fireColors[Math.floor(Math.random() * fireColors.length)];
			LK.effects.flashObject(this, randomColor, 400);
			// Add score bonus
			score += 75;
			combo += 1;
		};
	}
	effectButtons.push(effectButton);
	// If this is the second effect button (index 1) - LASER button
	if (i === 1) {
		effectButton.down = function (x, y, obj) {
			// Visual feedback - scale animation that returns to 1.5 instead of 1.0
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Cycle through laser show modes: 0 (off) -> 1 (slow) -> 2 (fast) -> 0 (off)
			laserShowMode = (laserShowMode + 1) % 3;
			// Flash effect with different colors based on mode
			var laserColors;
			if (laserShowMode === 0) {
				// Off mode - gray color
				laserColors = [0x888888];
			} else if (laserShowMode === 1) {
				// Slow mode - blue colors
				laserColors = [0x0000ff, 0x4444ff, 0x8888ff];
			} else {
				// Fast mode - rainbow colors
				laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
			}
			var randomColor = laserColors[Math.floor(Math.random() * laserColors.length)];
			LK.effects.flashObject(this, randomColor, 400);
			// Add score bonus
			score += 75;
			combo += 1;
		};
	}
	// If this is the third effect button (index 2)
	if (i === 2) {
		effectButton.down = function (x, y, obj) {
			// Visual feedback - scale animation that returns to 1.5 instead of 1.0
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Flash effect with random color
			var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
			var randomColor = colors[Math.floor(Math.random() * colors.length)];
			LK.effects.flashObject(this, randomColor, 400);
			// Add score bonus
			score += 75;
			combo += 1;
			// Toggle RGB flashing state
			isRgbFlashingActive = !isRgbFlashingActive;
		};
	}
	// Define variable to track the gifts button mode
	var giftsMode = 0; // 0: off, 1: balloons, 2: money, 3: fireworks
	// Reference the gifts effect button
	var giftsEffectButton;
	// If this is the fifth effect button (index 4) - UV button
	if (i === 4) {
		effectButton.down = function (x, y, obj) {
			// Visual feedback - scale animation that returns to 1.5 instead of 1.0
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Toggle UV effect state
			uvEffectActive = !uvEffectActive;
			// Flash effect with different colors based on mode
			var flashColor = uvEffectActive ? 0x8000ff : 0x888888; // Purple for ON, gray for OFF
			LK.effects.flashObject(this, flashColor, 400);
			// Add score bonus
			score += 75;
			combo += 1;
		};
		// Add a reference to the UV button for flashing in update
		effectButtons[4] = effectButton;
	}
	// If this is the sixth effect button (index 5) - GIFTS button
	if (i === 5) {
		giftsEffectButton = effectButton; // Store reference to gifts button
		effectButton.down = function (x, y, obj) {
			// Cycle through gift modes
			giftsMode = (giftsMode + 1) % 4;
			// Visual feedback - scale animation
			tween(this, {
				scaleX: 1.7,
				scaleY: 1.7
			}, {
				duration: 150,
				onFinish: function () {
					tween(this, {
						scaleX: 1.5,
						scaleY: 1.5
					}, {
						duration: 150
					});
				}.bind(this)
			});
			// Play FX sound
			LK.getSound('fx').play();
			// Add score bonus
			score += 100;
			combo += 1;
		};
	}
}
// Tablet between the two screens
var tabletAsset = LK.getAsset('tablet', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(tabletAsset);
// Ensure screens exist before positioning tablet
if (typeof leftDeckScreen !== 'undefined' && typeof rightDeckScreen !== 'undefined') {
	tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2;
	tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20;
} else {
	// Fallback positioning if screens are not defined
	tabletAsset.x = 2048 / 2;
	tabletAsset.y = 1366;
}
// Create rekordbox app instance
var rekordboxApp = new RekordboxApp();
game.addChild(rekordboxApp);
// Position and scale rekordbox app to fit tablet screen
rekordboxApp.x = tabletAsset.x - tabletAsset.width / 2 + 20 + 30;
rekordboxApp.y = tabletAsset.y - tabletAsset.height / 2 + 20 - 200 - 25;
// Create keyboard app instance
var keyboardApp = new KeyboardApp();
// Create keyboard app instance
var keyboardApp = new KeyboardApp();
var leftArrowButtonKeyboard = LK.getAsset('Leftarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
var rightArrowButtonKeyboard = LK.getAsset('Rightarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
var upArrowButtonKeyboard = LK.getAsset('UParrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
var downArrowButtonKeyboard = LK.getAsset('Downarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
var okButtonKeyboard = LK.getAsset('OKbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
// Position and scale keyboard app under the tracklist
keyboardApp.x = rekordboxApp.x - 777; // Align with rekordbox app and move left by 777 units
keyboardApp.y = rekordboxApp.y + rekordboxApp.height + 10 + 777 - 300 + 200 - 100 - 100; // Position below tracklist with spacing and move down by 777 units, then up by 300, then down by 200, then up by 100, then up by 100
keyboardApp.scaleX = rekordboxApp.scaleX * 1.1; // Match scale and adjust slightly
keyboardApp.scaleY = keyboardApp.scaleX; // Maintain aspect ratio
keyboardApp.visible = false; // Hide the keyboard initially
var keyboardButtonSpacing = 120; // Reduced spacing
leftArrowButtonKeyboard.x = keyboardApp.x + keyboardApp.width * keyboardApp.scaleX / 2 - keyboardButtonSpacing * 2 - 60;
leftArrowButtonKeyboard.y = keyboardApp.y - leftArrowButtonKeyboard.height / 2 - 20;
rightArrowButtonKeyboard.x = keyboardApp.x + keyboardApp.width * keyboardApp.scaleX / 2 - keyboardButtonSpacing - 30;
rightArrowButtonKeyboard.y = keyboardApp.y - rightArrowButtonKeyboard.height / 2 - 20;
upArrowButtonKeyboard.x = keyboardApp.x + keyboardApp.width * keyboardApp.scaleX / 2;
upArrowButtonKeyboard.y = keyboardApp.y - upArrowButtonKeyboard.height / 2 - 20;
downArrowButtonKeyboard.x = keyboardApp.x + keyboardApp.width * keyboardApp.scaleX / 2 + keyboardButtonSpacing;
downArrowButtonKeyboard.y = keyboardApp.y - downArrowButtonKeyboard.height / 2 - 20;
okButtonKeyboard.x = keyboardApp.x + keyboardApp.width * keyboardApp.scaleX / 2 + keyboardButtonSpacing * 2 + 30;
okButtonKeyboard.y = keyboardApp.y - okButtonKeyboard.height / 2 - 20;
// Scale up keyboard app to 3x size using tween animation
tween(keyboardApp, {
	scaleX: 3.0,
	scaleY: 3.0
}, {
	duration: 500,
	easing: tween.easeOut
});
// Scale down to better fit the tablet asset by 1.1
tween(rekordboxApp, {
	scaleX: 0.95 / 1.1,
	scaleY: 0.95 / 1.1
}, {
	duration: 500,
	easing: tween.easeOut
});
// Add tablet press functionality to pop up fullscreen tracklist
tabletAsset.down = function (x, y, obj) {
	// Create fullscreen tracklist popup
	var fullscreenTracklist = new RekordboxApp();
	// Add to game at top layer
	game.addChild(fullscreenTracklist);
	// Add menubar to the fullscreen tracklist
	if (fullscreenTracklist.menubar) {
		// Ensure the menubar from the original rekordboxApp is hidden when fullscreen is shown
		if (rekordboxApp.menubar) {
			rekordboxApp.menubar.visible = false;
		}
		// Remove the menubar from its parent (rekordboxApp) before adding to fullscreenTracklist
		if (rekordboxApp.menubar && rekordboxApp.menubar.parent) {
			rekordboxApp.menubar.parent.removeChild(rekordboxApp.menubar);
		}
		fullscreenTracklist.addChild(fullscreenTracklist.menubar);
		fullscreenTracklist.updateDisplay(); // Update display to position menubar
	}
	keyboardApp.visible = true; // Show the keyboard app
	leftArrowButtonKeyboard.visible = true;
	rightArrowButtonKeyboard.visible = true;
	upArrowButtonKeyboard.visible = true;
	downArrowButtonKeyboard.visible = true;
	okButtonKeyboard.visible = true;
	// Ensure keyboard is above the overlay and tracklist
	game.addChild(keyboardApp);
	game.addChild(leftArrowButtonKeyboard);
	game.addChild(rightArrowButtonKeyboard);
	game.addChild(upArrowButtonKeyboard);
	game.addChild(downArrowButtonKeyboard);
	game.addChild(okButtonKeyboard);
	// Position fullscreen - center on screen and move to top
	fullscreenTracklist.x = 2048 / 2 - 371 - 600; // Half of app width (742/2)
	fullscreenTracklist.y = 0 + 269; // Move to top of tablet height
	// Scale to fullscreen size
	fullscreenTracklist.scaleX = 2.5 * 1.05;
	fullscreenTracklist.scaleY = 2.5 * 1.05;
	// Create semi-transparent background overlay
	var overlay = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 2048,
		height: 2732
	});
	overlay.tint = 0x000000;
	overlay.alpha = 0.7;
	overlay.x = 0;
	overlay.y = 0;
	// Add overlay behind the app
	game.addChild(overlay);
	game.setChildIndex(overlay, game.children.indexOf(fullscreenTracklist) - 1);
	// Add X (close window) button to the full screen song maker app top right corner
	var closeButton = LK.getAsset('X', {
		anchorX: 1.0,
		anchorY: 0.0
	});
	closeButton.scaleX = 2.0;
	closeButton.scaleY = 2.0;
	closeButton.x = 2048 - 20; // 20px from right edge
	closeButton.y = 20; // 20px from top edge
	game.addChild(closeButton);
	// Add close functionality to the X button
	closeButton.down = function (x, y, obj) {
		// Remove fullscreen song maker app, overlay, and close button
		if (game.children.indexOf(fullscreenTracklist) !== -1) {
			game.removeChild(fullscreenTracklist);
		}
		if (game.children.indexOf(overlay) !== -1) {
			game.removeChild(overlay);
		}
		if (game.children.indexOf(closeButton) !== -1) {
			game.removeChild(closeButton);
		}
		keyboardApp.visible = false; // Hide the keyboard app
		leftArrowButtonKeyboard.visible = false;
		rightArrowButtonKeyboard.visible = false;
		upArrowButtonKeyboard.visible = false;
		downArrowButtonKeyboard.visible = false;
		okButtonKeyboard.visible = false;
		// Ensure the menubar on the original rekordboxApp is shown again
		if (rekordboxApp.menubar) {
			rekordboxApp.menubar.visible = true;
			rekordboxApp.updateDisplay(); // Update display to position menubar correctly
		}
	};
	// Add close functionality when clicking overlay
	// overlay.down = function (x, y, obj) {
	// // Remove fullscreen tracklist and overlay
	// if (game.children.indexOf(fullscreenTracklist) !== -1) {
	// game.removeChild(fullscreenTracklist);
	// }
	// if (game.children.indexOf(overlay) !== -1) {
	// game.removeChild(overlay);
	// }
	// if (game.children.indexOf(closeButton) !== -1) {
	// game.removeChild(closeButton);
	// }
	// keyboardApp.visible = false; // Hide the keyboard app
	// leftArrowButtonKeyboard.visible = false;
	// rightArrowButtonKeyboard.visible = false;
	// upArrowButtonKeyboard.visible = false;
	// downArrowButtonKeyboard.visible = false;
	// okButtonKeyboard.visible = false;
	// };
	// Visual feedback for tablet press
	tween(tabletAsset, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(tabletAsset, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 100
			});
		}
	});
	// Play sound effect
	LK.getSound('fx').play();
};
// --- IN Button under tablet screen ---
var inButton = LK.getAsset('in', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(inButton);
inButton.x = tabletAsset.x - 700 - 200 - 12;
inButton.y = tabletAsset.y + tabletAsset.height / 2 + inButton.height / 2 + 20 - 300 + 77;
// Add whitegray text: ADJUST above in buttons
var adjustTextLeft = new Text2('ADJUST', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
adjustTextLeft.anchor.set(0.5, 0.5);
adjustTextLeft.x = inButton.x + 107;
adjustTextLeft.y = inButton.y - inButton.height / 2 - 20; // Position above the button
game.addChild(adjustTextLeft);
// Add whitegray text: IN under in buttons
var inTextLeft = new Text2('IN', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
inTextLeft.anchor.set(0.5, 0.5);
inTextLeft.x = inButton.x;
inTextLeft.y = inButton.y + inButton.height / 2 + 20; // Position under the button
game.addChild(inTextLeft);
// Add functionality to IN button
inButton.down = function (x, y, obj) {
	// Stop any existing flashing effect first
	tween.stop(inButton, {
		tint: true
	});
	// Start flashing orange light effect
	function createOrangeFlash() {
		tween(inButton, {
			tint: 0xff8000 // Orange color
		}, {
			duration: 300,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(inButton, {
					tint: 0xffffff // Back to white/normal
				}, {
					duration: 300,
					easing: tween.easeInOut,
					onFinish: createOrangeFlash // Continue flashing
				});
			}
		});
	}
	createOrangeFlash();
	// Add score bonus
	score += 25;
	combo += 1;
};
// --- OUT Button under tablet screen ---
var outButton = LK.getAsset('Out', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(outButton);
outButton.x = tabletAsset.x + 700 + 200;
outButton.y = tabletAsset.y + tabletAsset.height / 2 + outButton.height / 2 + 20 - 300 + 77;
// --- Right IN Button to the left of right OUT button ---
var rightInButton = LK.getAsset('in', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightInButton);
rightInButton.x = outButton.x - 192 - 200 - 12;
rightInButton.y = outButton.y;
// Create right ADJUST text now that rightInButton is defined
var adjustTextRight = new Text2('ADJUST', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
adjustTextRight.anchor.set(0.5, 0.5);
adjustTextRight.x = rightInButton.x + 107;
adjustTextRight.y = rightInButton.y - rightInButton.height / 2 - 20; // Position above the button
game.addChild(adjustTextRight);
// Add whitegray text: IN under right in buttons
var inTextRight = new Text2('IN', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
inTextRight.anchor.set(0.5, 0.5);
inTextRight.x = rightInButton.x;
inTextRight.y = rightInButton.y + rightInButton.height / 2 + 20; // Position under the button
game.addChild(inTextRight);
// Add functionality to right IN button
rightInButton.down = function (x, y, obj) {
	// Stop any existing flashing effect first
	tween.stop(rightInButton, {
		tint: true
	});
	// Start flashing orange light effect
	function createOrangeFlash() {
		tween(rightInButton, {
			tint: 0xff8000 // Orange color
		}, {
			duration: 300,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(rightInButton, {
					tint: 0xffffff // Back to white/normal
				}, {
					duration: 300,
					easing: tween.easeInOut,
					onFinish: createOrangeFlash // Continue flashing
				});
			}
		});
	}
	createOrangeFlash();
	// Add score bonus
	score += 25;
	combo += 1;
};
// --- OUT Button above left deck ---
var outButtonLeft = LK.getAsset('Out', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(outButtonLeft);
outButtonLeft.x = leftDeckAsset.x + 192 - 200;
outButtonLeft.y = inButton.y;
// --- Loop Exit Button on the right side of left out button ---
var loopExitButton = LK.getAsset('LoopExit', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(loopExitButton);
loopExitButton.x = outButtonLeft.x + 150 + 50;
loopExitButton.y = outButtonLeft.y;
// Add whitegray text: OUT under left out button
var outTextLeft = new Text2('OUT', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
outTextLeft.anchor.set(0.5, 0.5);
outTextLeft.x = outButtonLeft.x;
outTextLeft.y = inTextLeft.y - 3; // Position at same height as IN text, moved up by 3 units
game.addChild(outTextLeft);
// Add whitegray text: LOOP between left out button and left loop exit button
var loopTextLeft = new Text2('LOOP', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
loopTextLeft.anchor.set(0.5, 0.5);
loopTextLeft.x = (outButtonLeft.x + loopExitButton.x) / 2;
loopTextLeft.y = adjustTextLeft.y; // Position at same height as ADJUST text
game.addChild(loopTextLeft);
// Add whitegray text: EXIT under left loop exit button
var exitTextLeft = new Text2('EXIT', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
exitTextLeft.anchor.set(0.5, 0.5);
exitTextLeft.x = loopExitButton.x;
exitTextLeft.y = loopExitButton.y + loopExitButton.height / 2 + 20; // Position under the button
game.addChild(exitTextLeft);
// Add functionality to loop exit button
loopExitButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(loopExitButton, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(loopExitButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with purple color
	LK.effects.flashObject(loopExitButton, 0x8000ff, 300);
	// Add score bonus
	score += 30;
	combo += 1;
};
// Add functionality to left OUT button
outButtonLeft.down = function (x, y, obj) {
	// Stop any existing flashing effect first
	tween.stop(outButtonLeft, {
		tint: true
	});
	// Stop flashing the corresponding 'in' button
	tween.stop(inButton, {
		tint: true
	});
	inButton.tint = 0xffffff; // Reset tint to normal
	// Start flashing orange light effect
	function createOrangeFlash() {
		tween(outButtonLeft, {
			tint: 0xff8000 // Orange color
		}, {
			duration: 300,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(outButtonLeft, {
					tint: 0xffffff // Back to white/normal
				}, {
					duration: 300,
					easing: tween.easeInOut,
					onFinish: createOrangeFlash // Continue flashing
				});
			}
		});
	}
	createOrangeFlash();
	// Add score bonus
	score += 25;
	combo += 1;
};
// --- Right OUT Button ---
var rightOutButton = LK.getAsset('Out', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightOutButton);
rightOutButton.x = rightDeckAsset.x + 192 - 200;
rightOutButton.y = inButton.y;
// --- Right Loop Exit Button next to right out button ---
var rightLoopExitButton = LK.getAsset('LoopExit', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightLoopExitButton);
rightLoopExitButton.x = rightOutButton.x + 150 + 50 + 5;
rightLoopExitButton.y = rightOutButton.y;
// Add functionality to right loop exit button
rightLoopExitButton.down = function (x, y, obj) {
	// Visual feedback - scale animation
	tween(rightLoopExitButton, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(rightLoopExitButton, {
				scaleX: 1,
				scaleY: 1
			}, {
				duration: 100
			});
		}
	});
	// Flash effect with purple color
	LK.effects.flashObject(rightLoopExitButton, 0x8000ff, 300);
	// Add score bonus
	score += 30;
	combo += 1;
};
// Add whitegray text: OUT under right out button
var outTextRight = new Text2('OUT', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
outTextRight.anchor.set(0.5, 0.5);
outTextRight.x = rightOutButton.x;
outTextRight.y = inTextRight.y - 3; // Position at same height as IN text, moved up by 3 units
game.addChild(outTextRight);
// Add whitegray text: LOOP between right out button and right loop exit button
var loopTextRight = new Text2('LOOP', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
loopTextRight.anchor.set(0.5, 0.5);
loopTextRight.x = (rightOutButton.x + rightLoopExitButton.x) / 2;
loopTextRight.y = adjustTextRight.y; // Position at same height as ADJUST text
game.addChild(loopTextRight);
// Add whitegray text: EXIT under right loop exit button
var exitTextRight = new Text2('EXIT', {
	size: 24,
	fill: 0xCCCCCC // Whitegray color
});
exitTextRight.anchor.set(0.5, 0.5);
exitTextRight.x = rightLoopExitButton.x;
exitTextRight.y = rightLoopExitButton.y + rightLoopExitButton.height / 2 + 20; // Position under the button
game.addChild(exitTextRight);
// Add functionality to right OUT button
rightOutButton.down = function (x, y, obj) {
	// Stop any existing flashing effect first
	tween.stop(rightOutButton, {
		tint: true
	});
	// Stop flashing the corresponding 'in' button
	tween.stop(rightInButton, {
		tint: true
	});
	rightInButton.tint = 0xffffff; // Reset tint to normal
	// Start flashing orange light effect
	function createOrangeFlash() {
		tween(rightOutButton, {
			tint: 0xff8000 // Orange color
		}, {
			duration: 300,
			easing: tween.easeInOut,
			onFinish: function onFinish() {
				tween(rightOutButton, {
					tint: 0xffffff // Back to white/normal
				}, {
					duration: 300,
					easing: tween.easeInOut,
					onFinish: createOrangeFlash // Continue flashing
				});
			}
		});
	}
	createOrangeFlash();
	// Add score bonus
	score += 25;
	combo += 1;
};
// --- Connecting Lines between In and Out Buttons ---
// Line between inButton and outButtonLeft
var lineInToOutLeft = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: Math.abs(outButtonLeft.x - inButton.x) - 120,
	height: 3
});
game.addChild(lineInToOutLeft);
lineInToOutLeft.x = (inButton.x + outButtonLeft.x) / 2;
lineInToOutLeft.y = inButton.y;
lineInToOutLeft.tint = 0x888888; // Gray color
// Line between rightInButton and rightOutButton
var lineRightInToOut = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: Math.abs(rightOutButton.x - rightInButton.x) - 120,
	height: 3
});
game.addChild(lineRightInToOut);
lineRightInToOut.x = (rightInButton.x + rightOutButton.x) / 2;
lineRightInToOut.y = rightInButton.y;
lineRightInToOut.tint = 0x888888; // Gray color
// Add white notches for horizontal crossfader (top side)
var horizontalCrossfaderTrack = crossfader; // Use the existing crossfader
var notchSpacingHorizontal = horizontalCrossfaderTrack.width / 10; // 10 notches
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 3,
		// Notch width
		height: 20 // Notch height
	});
	notch.tint = 0xFFFFFF; // White color
	notch.x = horizontalCrossfaderTrack.x - horizontalCrossfaderTrack.width / 2 + i * notchSpacingHorizontal; // Position along the top edge
	notch.y = horizontalCrossfaderTrack.y - 20; // Position above the track
	game.addChild(notch);
}
// Add white notches for horizontal crossfader (bottom side)
var horizontalCrossfaderTrack = crossfader; // Use the existing crossfader
var notchSpacingHorizontal = horizontalCrossfaderTrack.width / 10; // 10 notches
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 3,
		// Notch width
		height: 20 // Notch height
	});
	notch.tint = 0xFFFFFF; // White color
	notch.x = horizontalCrossfaderTrack.x - horizontalCrossfaderTrack.width / 2 + i * notchSpacingHorizontal; // Position along the top edge
	notch.y = horizontalCrossfaderTrack.y + 20; // Position below the track
	game.addChild(notch);
}
// Horizontal crossfader knob is already managed by the crossfader object
// Add a vertical crossfader track
var verticalCrossfaderTrack = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 12,
	height: 500
});
// Ensure horizontal crossfader knob is above the notches
game.addChild(crossfader);
crossfader.x = 2048 / 2;
crossfader.y = crossfaderY + 333 - 124 + 100;
crossfader.setValue(0.5);
// Add a vertical crossfader track
var verticalCrossfaderTrack = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 12,
	height: 500
});
game.addChild(verticalCrossfaderTrack);
verticalCrossfaderTrack.x = djdeckAsset.x - 250;
verticalCrossfaderTrack.y = djdeckAsset.y - 100 + 400;
verticalCrossfaderTrack.tint = 0xFFFFFF;
// Add white notches for scaling
var notchSpacing = verticalCrossfaderTrack.height / 10; // 10 notches
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 20,
		// Notch width
		height: 3 // Notch height
	});
	notch.tint = 0xFFFFFF; // White color
	notch.x = verticalCrossfaderTrack.x + 20; // Position to the right of the track
	notch.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + i * notchSpacing;
	game.addChild(notch);
}
// Add white notches for scaling on the other side of the track
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 20,
		height: 3
	});
	notch.tint = 0xFFFFFF;
	notch.x = verticalCrossfaderTrack.x - 20; // Position to the left of the track
	notch.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + i * notchSpacing;
	game.addChild(notch);
}
// Add a knob onto the vertical crossfader track
var verticalCrossfaderKnob = LK.getAsset('crossfaderKnob', {
	anchorX: 0.5,
	anchorY: 0.5,
	rotation: Math.PI / 2
});
verticalCrossfaderKnob.x = verticalCrossfaderTrack.x;
verticalCrossfaderKnob.y = verticalCrossfaderTrack.y;
// Add a knob onto the vertical crossfader track
var verticalCrossfaderKnob = LK.getAsset('crossfaderKnob', {
	anchorX: 0.5,
	anchorY: 0.5,
	rotation: Math.PI / 2
});
verticalCrossfaderKnob.x = verticalCrossfaderTrack.x;
verticalCrossfaderKnob.y = verticalCrossfaderTrack.y;
game.addChild(verticalCrossfaderKnob);
// Ensure knob is above the notches by bringing it to the top of the children list of game
game.setChildIndex(verticalCrossfaderKnob, game.children.length - 1);
// Add white "-" text under the left vertical crossfader
var leftCrossfaderMinusText = new Text2('-', {
	size: 40,
	fill: 0xFFFFFF // White color
});
leftCrossfaderMinusText.scaleX = 4.0; // Increased width by 2x
leftCrossfaderMinusText.scaleY = 2.0; // Increased height by 2x
leftCrossfaderMinusText.anchor.set(0.5, 0.5);
leftCrossfaderMinusText.x = verticalCrossfaderTrack.x;
leftCrossfaderMinusText.y = verticalCrossfaderTrack.y + verticalCrossfaderTrack.height / 2 + 30; // Position under the crossfader
game.addChild(leftCrossfaderMinusText);
// Add white "+" text above the left vertical crossfader
var leftCrossfaderPlusText = new Text2('+', {
	size: 40,
	fill: 0xFFFFFF // White color
});
leftCrossfaderPlusText.anchor.set(0.5, 0.5);
leftCrossfaderPlusText.x = verticalCrossfaderTrack.x;
leftCrossfaderPlusText.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 - 30; // Position above the crossfader
game.addChild(leftCrossfaderPlusText);
// Add white TEMPO text above the left vertical crossfader plus sign
var leftCrossfaderTempoText = new Text2('TEMPO', {
	size: 30,
	fill: 0xFFFFFF // White color
});
leftCrossfaderTempoText.anchor.set(0.5, 0.5);
leftCrossfaderTempoText.x = verticalCrossfaderTrack.x;
leftCrossfaderTempoText.y = leftCrossfaderPlusText.y - 40; // Position above the plus sign
game.addChild(leftCrossfaderTempoText);
// Add a second vertical crossfader track
var verticalCrossfaderTrack2 = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 12,
	height: 500
});
game.addChild(verticalCrossfaderTrack2);
verticalCrossfaderTrack2.x = djdeckAsset.x + 250; // Position on the right side
verticalCrossfaderTrack2.y = djdeckAsset.y - 100 + 400;
verticalCrossfaderTrack2.tint = 0xFFFFFF;
// Add white notches for scaling
var notchSpacing = verticalCrossfaderTrack2.height / 10; // 10 notches
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 20,
		// Notch width
		height: 3 // Notch height
	});
	notch.tint = 0xFFFFFF; // White color
	notch.x = verticalCrossfaderTrack2.x - 20; // Position to the left of the track
	notch.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + i * notchSpacing;
	game.addChild(notch);
}
// Add white notches for scaling on the other side of the track
for (var i = 0; i <= 10; i++) {
	var notch = LK.getAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 20,
		height: 3
	});
	notch.tint = 0xFFFFFF;
	notch.x = verticalCrossfaderTrack2.x + 20; // Position to the right of the track
	notch.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + i * notchSpacing;
	game.addChild(notch);
}
// Add a knob onto the second vertical crossfader track
var verticalCrossfaderKnob2 = LK.getAsset('crossfaderKnob', {
	anchorX: 0.5,
	anchorY: 0.5,
	rotation: Math.PI / 2
});
verticalCrossfaderKnob2.x = verticalCrossfaderTrack2.x;
verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y;
// Add a knob onto the second vertical crossfader track
var verticalCrossfaderKnob2 = LK.getAsset('crossfaderKnob', {
	anchorX: 0.5,
	anchorY: 0.5,
	rotation: Math.PI / 2
});
verticalCrossfaderKnob2.x = verticalCrossfaderTrack2.x;
verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y;
game.addChild(verticalCrossfaderKnob2);
// Ensure knob is above the notches by bringing it to the top of the children list of game
game.setChildIndex(verticalCrossfaderKnob2, game.children.length - 1);
// Add white Balance text under the horizontal crossfader
// Add white "-" text under the right vertical crossfader
var rightCrossfaderMinusText = new Text2('-', {
	size: 40,
	fill: 0xFFFFFF // White color
});
rightCrossfaderMinusText.scaleX = 4.0; // Increased width by 2x
rightCrossfaderMinusText.scaleY = 2.0; // Increased height by 2x
rightCrossfaderMinusText.anchor.set(0.5, 0.5);
rightCrossfaderMinusText.x = verticalCrossfaderTrack2.x;
rightCrossfaderMinusText.y = verticalCrossfaderTrack2.y + verticalCrossfaderTrack2.height / 2 + 30; // Position under the crossfader
game.addChild(rightCrossfaderMinusText);
// Add white "+" text above the right vertical crossfader
var rightCrossfaderPlusText = new Text2('+', {
	size: 40,
	fill: 0xFFFFFF // White color
});
rightCrossfaderPlusText.anchor.set(0.5, 0.5);
rightCrossfaderPlusText.x = verticalCrossfaderTrack2.x;
rightCrossfaderPlusText.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 - 30; // Position above the crossfader
game.addChild(rightCrossfaderPlusText);
// Add white TEMPO text above the right vertical crossfader plus sign
var rightCrossfaderTempoText = new Text2('TEMPO', {
	size: 30,
	fill: 0xFFFFFF // White color
});
rightCrossfaderTempoText.anchor.set(0.5, 0.5);
rightCrossfaderTempoText.x = verticalCrossfaderTrack2.x;
rightCrossfaderTempoText.y = rightCrossfaderPlusText.y - 40; // Position above the plus sign
game.addChild(rightCrossfaderTempoText);
// Add filter knobs
var leftFilterKnobHigh = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftFilterKnobHigh);
leftFilterKnobHigh.anchor.set(0.5, 0.5);
leftFilterKnobHigh.x = leftDeckAsset.x + 190 + 400 + 12;
leftFilterKnobHigh.y = leftDeckAsset.y - 100 + 25 + 130;
// Add white HIGH text under the HIGH filter knob
var highTextLeft = new Text2('HIGH', {
	size: 24,
	fill: 0xFFFFFF // White color
});
highTextLeft.anchor.set(0.5, 0.5);
highTextLeft.x = leftFilterKnobHigh.x;
highTextLeft.y = leftFilterKnobHigh.y + leftFilterKnobHigh.height / 2 + 20; // Position under the knob
game.addChild(highTextLeft);
// Add white FILTER text above the HIGH filter knobs, horizontally between them
var filterTextLeft = new Text2('FILTER', {
	size: 30,
	fill: 0xFFFFFF // White color
});
filterTextLeft.anchor.set(0.5, 0.5);
filterTextLeft.x = leftFilterKnobHigh.x + 100; // Position text to the right of the knob for horizontal alignment
filterTextLeft.y = leftFilterKnobHigh.y - leftFilterKnobHigh.height / 2 - 40 + 130 - 120; // Position above the knob
game.addChild(filterTextLeft);
tween(leftFilterKnobHigh, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var leftFilterKnobMid = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftFilterKnobMid);
leftFilterKnobMid.anchor.set(0.5, 0.5);
leftFilterKnobMid.x = leftDeckAsset.x + 190 + 400 + 12;
leftFilterKnobMid.y = leftDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12;
tween(leftFilterKnobMid, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var leftFilterKnobLow = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(leftFilterKnobLow);
leftFilterKnobLow.anchor.set(0.5, 0.5);
leftFilterKnobLow.x = leftDeckAsset.x + 190 + 400 + 12;
leftFilterKnobLow.y = leftDeckAsset.y + 100 + 120 + 120 + 77;
// Add white LOW text under the LOW filter knob
var lowTextLeft = new Text2('LOW', {
	size: 24,
	fill: 0xFFFFFF // White color
});
lowTextLeft.anchor.set(0.5, 0.5);
lowTextLeft.x = leftFilterKnobLow.x;
lowTextLeft.y = leftFilterKnobLow.y + leftFilterKnobLow.height / 2 + 20; // Position under the knob
game.addChild(lowTextLeft);
tween(leftFilterKnobLow, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var rightFilterKnobHigh = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightFilterKnobHigh);
rightFilterKnobHigh.anchor.set(0.5, 0.5);
rightFilterKnobHigh.x = rightDeckAsset.x - 190 - 400 - 12;
rightFilterKnobHigh.y = rightDeckAsset.y - 100 + 25 + 130;
// Add white HIGH text under the HIGH filter knobs (Right side)
var highTextRight = new Text2('HIGH', {
	size: 24,
	fill: 0xFFFFFF // White color
});
highTextRight.anchor.set(0.5, 0.5);
highTextRight.x = rightFilterKnobHigh.x;
highTextRight.y = rightFilterKnobHigh.y + rightFilterKnobHigh.height / 2 + 20; // Position under the knob
game.addChild(highTextRight);
// Add white FILTER text above the HIGH filter knobs, horizontally between them (Right side)
var filterTextRight = new Text2('FILTER', {
	size: 30,
	fill: 0xFFFFFF // White color
});
filterTextRight.anchor.set(0.5, 0.5);
filterTextRight.x = rightFilterKnobHigh.x - 100; // Position text to the left of the knob for horizontal alignment
filterTextRight.y = rightFilterKnobHigh.y - rightFilterKnobHigh.height / 2 - 40 + 130 - 120; // Position above the knob
game.addChild(filterTextRight);
tween(rightFilterKnobHigh, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var rightFilterKnobMid = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightFilterKnobMid);
rightFilterKnobMid.anchor.set(0.5, 0.5);
rightFilterKnobMid.x = rightDeckAsset.x - 190 - 400 - 12;
rightFilterKnobMid.y = rightDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12;
tween(rightFilterKnobMid, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var rightFilterKnobLow = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(rightFilterKnobLow);
rightFilterKnobLow.anchor.set(0.5, 0.5);
rightFilterKnobLow.x = rightDeckAsset.x - 190 - 400 - 12;
rightFilterKnobLow.y = rightDeckAsset.y + 100 + 120 + 120 + 77;
// Add white LOW text under the LOW filter knob (Right side)
var lowTextRight = new Text2('LOW', {
	size: 24,
	fill: 0xFFFFFF // White color
});
lowTextRight.anchor.set(0.5, 0.5);
lowTextRight.x = rightFilterKnobLow.x;
lowTextRight.y = rightFilterKnobLow.y + rightFilterKnobLow.height / 2 + 20; // Position under the knob
game.addChild(lowTextRight);
tween(rightFilterKnobLow, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
function setupFilterKnobInteraction(knob) {
	knob.down = function (x, y, obj) {
		tween.stop(this);
		// Scale down the previously selected filter knob if it exists and is not the current one
		if (lastSelectedFilterKnob && lastSelectedFilterKnob !== this) {
			tween.stop(lastSelectedFilterKnob);
			tween(lastSelectedFilterKnob, {
				scaleX: 1.2,
				scaleY: 1.2
			}, {
				duration: 100,
				easing: tween.easeOut
			});
		}
		lastSelectedFilterKnob = this;
		tween(this, {
			scaleX: 2.4,
			scaleY: 2.4
		}, {
			duration: 100,
			easing: tween.easeOut
		});
	};
	knob.up = function (x, y, obj) {
		tween.stop(this);
		// Do not scale down on up, keep enlarged
	};
}
setupFilterKnobInteraction(leftFilterKnobHigh);
setupFilterKnobInteraction(leftFilterKnobMid);
setupFilterKnobInteraction(leftFilterKnobLow);
setupFilterKnobInteraction(rightFilterKnobHigh);
setupFilterKnobInteraction(rightFilterKnobMid);
setupFilterKnobInteraction(rightFilterKnobLow);
// --- Vertical crossfader state ---
var verticalCrossfaderValue = 0.5; // 0 = top, 1 = bottom
var verticalCrossfaderDragging = false;
// Second vertical crossfader state
var verticalCrossfaderValue2 = 0.5; // 0 = top, 1 = bottom
var verticalCrossfaderDragging2 = false;
var isRgbFlashingActive = false; // State variable for RGB flashing effect
var laserShowMode = 0; // Laser show state: 0 = off, 1 = slow, 2 = fast
var fireEffectMode = 0; // Fire effect state: 0 = off, 1 = normal flames, 2 = big flames
var lastSelectedFilterKnob = null; // Variable to track the last selected filter knob.
var uvEffectActive = false; // UV effect state: false = off, true = on
var uvBackgroundColorTween = null; // Variable to hold the background color tween
// Add vertical crossfader functionality with improved touch detection
verticalCrossfaderTrack.down = function (x, y, obj) {
	verticalCrossfaderDragging = true;
	// Use relative position within track bounds for precise control
	var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height;
	verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY));
	// Update knob position smoothly
	verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height;
};
verticalCrossfaderTrack.up = function (x, y, obj) {
	verticalCrossfaderDragging = false;
};
verticalCrossfaderTrack.move = function (x, y, obj) {
	if (verticalCrossfaderDragging) {
		// Use relative position within track bounds for precise control
		var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height;
		verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY));
		// Update knob position smoothly
		verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height;
	}
};
// Add vertical crossfader functionality for the second crossfader with improved touch detection
verticalCrossfaderTrack2.down = function (x, y, obj) {
	verticalCrossfaderDragging2 = true;
	// Use relative position within track bounds for precise control
	var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height;
	verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY));
	// Update knob position smoothly
	verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height;
};
verticalCrossfaderTrack2.up = function (x, y, obj) {
	verticalCrossfaderDragging2 = false;
};
verticalCrossfaderTrack2.move = function (x, y, obj) {
	if (verticalCrossfaderDragging2) {
		// Use relative position within track bounds for precise control
		var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height;
		verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY));
		// Update knob position smoothly
		verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height;
	}
};
var leftEqualizer = new EqualizerBars();
game.addChild(leftEqualizer);
leftEqualizer.x = leftDeckScreen.x;
leftEqualizer.y = leftDeckScreen.y + 50;
// Add equalizer to right deck screen
var rightEqualizer = new EqualizerBars();
game.addChild(rightEqualizer);
rightEqualizer.x = rightDeckScreen.x;
rightEqualizer.y = rightDeckScreen.y + 50;
// Ensure deck platters are always at the top of the display order
if (game.children.indexOf(leftDeck) !== -1) {
	game.setChildIndex(leftDeck, game.children.length - 1);
}
if (game.children.indexOf(rightDeck) !== -1) {
	game.setChildIndex(rightDeck, game.children.length - 1);
}
// --- Game update ---
game.update = function () {
	// Update discoball animation
	discoball.update();
	// Update fog effect animation
	fogEffect.update();
	// Update confetti effect animation
	confettiEffect.update();
	// Update balloons animation
	if (balloons) {
		for (var i = 0; i < balloons.length; i++) {
			balloons[i].update();
		}
	}
	// Update money particles animation
	if (moneyParticles) {
		for (var i = 0; i < moneyParticles.length; i++) {
			moneyParticles[i].update();
		}
	}
	// Update first dancing people line animations (first set created)
	for (var i = 0; i < Math.min(dancingPeople.length, 8); i++) {
		var person = dancingPeople[i];
		// Check if UV effect is active
		if (uvEffectActive) {
			// If not already a UV person, swap to UV asset
			if (!person.isUV) {
				var uvAssetId = i % 2 === 0 ? 'UVW' : 'UWM';
				var currentScaleX = person.scale.x;
				var currentScaleY = person.scale.y;
				var currentRotation = person.rotation;
				var currentAlpha = person.alpha;
				var currentX = person.x;
				var currentY = person.y;
				var currentBaseY = person.baseY;
				var currentPhaseOffset = person.phaseOffset;
				// Remove the old graphic child
				if (person.children.length > 0) {
					person.removeChildAt(0);
				}
				// Add the new UV graphic child
				var uvGraphic = person.attachAsset(uvAssetId, {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Restore previous transform and animation properties
				person.scale.set(currentScaleX, currentScaleY);
				person.rotation = currentRotation;
				person.alpha = currentAlpha;
				person.x = currentX;
				person.y = currentY;
				person.baseY = currentBaseY;
				person.phaseOffset = currentPhaseOffset;
				person.isUV = true;
			}
		} else {
			// If UV effect is off and it's a UV person, swap back to normal asset
			if (person.isUV) {
				var normalAssetId = i % 2 === 0 ? 'dancingWoman' : 'dancingMan';
				var currentScaleX = person.scale.x;
				var currentScaleY = person.scale.y;
				var currentRotation = person.rotation;
				var currentAlpha = person.alpha;
				var currentX = person.x;
				var currentY = person.y;
				var currentBaseY = person.baseY;
				var currentPhaseOffset = person.phaseOffset;
				// Remove the old graphic child
				if (person.children.length > 0) {
					person.removeChildAt(0);
				}
				// Add the new normal graphic child
				var normalGraphic = person.attachAsset(normalAssetId, {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Restore previous transform and animation properties
				person.scale.set(currentScaleX, currentScaleY);
				person.rotation = currentRotation;
				person.alpha = currentAlpha;
				person.x = currentX;
				person.y = currentY;
				person.baseY = currentBaseY;
				person.phaseOffset = currentPhaseOffset;
				person.isUV = false;
			}
		}
		person.update();
	}
	// Update second dancing people line animations (second set created) - swap with UV assets when UV is active
	for (var i = 8; i < dancingPeople.length; i++) {
		var person = dancingPeople[i];
		// Check if UV effect is active
		if (uvEffectActive) {
			// If not already a UV person, swap to UV asset
			if (!person.isUV) {
				var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM';
				var currentScaleX = person.scale.x;
				var currentScaleY = person.scale.y;
				var currentRotation = person.rotation;
				var currentAlpha = person.alpha;
				var currentX = person.x;
				var currentY = person.y;
				var currentBaseY = person.baseY;
				var currentPhaseOffset = person.phaseOffset;
				// Remove the old graphic child
				if (person.children.length > 0) {
					person.removeChildAt(0);
				}
				// Add the new UV graphic child
				var uvGraphic = person.attachAsset(uvAssetId, {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Restore previous transform and animation properties
				person.scale.set(currentScaleX, currentScaleY);
				person.rotation = currentRotation;
				person.alpha = currentAlpha;
				person.x = currentX;
				person.y = currentY;
				person.baseY = currentBaseY;
				person.phaseOffset = currentPhaseOffset;
				person.isUV = true;
			}
		} else {
			// If UV effect is off and it's a UV person, swap back to normal asset
			if (person.isUV) {
				var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan';
				var currentScaleX = person.scale.x;
				var currentScaleY = person.scale.y;
				var currentRotation = person.rotation;
				var currentAlpha = person.alpha;
				var currentX = person.x;
				var currentY = person.y;
				var currentBaseY = person.baseY;
				var currentPhaseOffset = person.phaseOffset;
				// Remove the old graphic child
				if (person.children.length > 0) {
					person.removeChildAt(0);
				}
				// Add the new normal graphic child
				var normalGraphic = person.attachAsset(normalAssetId, {
					anchorX: 0.5,
					anchorY: 1.0
				});
				// Restore previous transform and animation properties
				person.scale.set(currentScaleX, currentScaleY);
				person.rotation = currentRotation;
				person.alpha = currentAlpha;
				person.x = currentX;
				person.y = currentY;
				person.baseY = currentBaseY;
				person.phaseOffset = currentPhaseOffset;
				person.isUV = false;
			}
		}
		person.update();
	}
	// Animate extra white particles (subtle sparkle and movement)
	if (extraWhiteParticles) {
		var t = Date.now() * 0.001;
		for (var i = 0; i < extraWhiteParticles.length; i++) {
			var p = extraWhiteParticles[i];
			// Subtle float and twinkle
			p.obj.x = p.baseX + Math.sin(t * 1.2 + p.phase + i) * 8;
			p.obj.y = p.baseY + Math.cos(t * 1.1 + p.phase + i * 0.7) * 6;
			p.obj.alpha = p.baseAlpha + 0.10 * Math.sin(t * 2.2 + p.phase + i * 0.5);
			var s = p.baseSize * (0.95 + 0.12 * Math.sin(t * 1.7 + p.phase + i));
			p.obj.width = s;
			p.obj.height = s;
		}
	}
	// Animate flashing white reflectors
	if (reflectors) {
		var t = Date.now() * 0.001;
		for (var i = 0; i < reflectors.length; i++) {
			// Staggered flash, each reflector flashes in sequence
			var phase = t * 2.2 + i * 0.5;
			// Use a sharp pulse for flash, then fade out
			var flash = Math.max(0, Math.sin(phase));
			// Sharpen the flash curve for a strobe effect
			flash = Math.pow(flash, 6);
			reflectors[i].alpha = 0.18 + 0.82 * flash;
			// Animate as perfect circles for extra pop (half size base)
			var size = 60 + 20 * flash;
			// Prevent reflectors from covering the discoball
			// Compute distance from reflector to discoball center
			var dx = reflectors[i].x - discoball.x;
			var dy = reflectors[i].y - discoball.y;
			var dist = Math.sqrt(dx * dx + dy * dy);
			// Discoball's visible radius (scaled)
			var discoballRadius = 150 * discoball.scaleX; // matches Discoball class
			// If reflector would overlap discoball, shrink it so it cannot cover
			if (dist < discoballRadius + size / 2) {
				// Max allowed size so edge of reflector does not enter discoball
				var maxSize = Math.max(0, 2 * (dist - discoballRadius));
				if (size > maxSize) size = maxSize;
				if (size < 0) size = 0;
			}
			reflectors[i].width = size;
			reflectors[i].height = size;
		}
	}
	// Update laser show animation
	laserShow.update();
	// Update fireworks effect animation
	fireworksEffect.update();
	fireworksEffect2.update();
	fireworksEffect3.update();
	fireworksEffect4.update();
	// Update Ferris wheel animation
	if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) {
		// Defensive check
		ferrisWheel.update();
	}
	// Update smoke diffuser animation
	// --- Synchronize all smoke diffusers so they emit smoke puffs at the same time ---
	if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) {
		// Use the first smoke diffuser as the "master" for timing
		var now = Date.now();
		// All diffusers share the same "now" and will create puffs in sync
		for (var i = 0; i < smokeDiffusers.length; i++) {
			var sd = smokeDiffusers[i];
			// Patch: override the update method to use the same "now" for all
			if (sd && typeof sd.update === 'function') {
				// Save original Date.now
				var origDateNow = Date.now;
				Date.now = function () {
					return now;
				};
				sd.update();
				Date.now = origDateNow;
			}
		}
	}
	// Update flamethrower animation
	if (typeof flamethrower !== 'undefined' && flamethrower.update) {
		flamethrower.update();
	}
	// Update decks
	leftDeck.update();
	rightDeck.update();
	// Update equalizers
	leftEqualizer.update();
	rightEqualizer.update();
	// Simulate beat
	var now = Date.now();
	beatTimer += now - lastTickTime;
	lastTickTime = now;
	if (beatTimer >= beatInterval) {
		beatTimer -= beatInterval;
		leftDeck.flashBeat();
		rightDeck.flashBeat();
		LK.getSound('beat').play();
		// Energy drops if not scratching or mixing
		if (!leftDeck.isScratching && !rightDeck.isScratching && crossfader.value > 0.2 && crossfader.value < 0.8) {
			energy -= 2;
			combo = 0;
		} else {
			// Combo up if scratching or crossfading
			combo += 1;
			score += 10 * combo;
			energy += 2;
			if (energy > 100) energy = 100;
		}
		if (energy < 0) energy = 0;
		// Update visuals
		scoreTxt.setText('Score: ' + score);
		if (combo > 1) {
			comboTxt.setText('Combo x' + combo);
		} else {
			comboTxt.setText('');
		}
	}
	// Update energy bar
	energyBar.width = 600 * (energy / 100);
	// Crossfader logic: adjust music volumes
	trackAVol = 1 - crossfader.value;
	trackBVol = crossfader.value;
	// (In a real game, would set music volumes here, but LK handles music globally.)
	// End game if energy is 0
	if (energy <= 0) {
		// Game over event disabled
	}
	// Animate background color
	var time = Date.now() * 0.0005; // Time for animation
	var hue = time * 360 % 360; // Cycle through hues over time
	var rgb = hsvToRgb(hue / 360, 0.6, 0.5); // Convert HSV to RGB (adjust saturation and value for desired effect)
	// Only animate the background color if RGB flashing is active on the third effect button
	if (isRgbFlashingActive) {
		game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]);
	} else if (uvEffectActive) {
		// If UV effect is active, flash between indigo blue and purple
		var uvTime = Date.now() * 0.002; // Faster flashing for UV
		var uvHue = uvTime * 360 % 360;
		// Cycle between indigo (around 240 hue) and purple (around 270 hue)
		var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30;
		targetHue = (targetHue + 360) % 360;
		var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors
		game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]);
	} else {
		// If neither is active, set a default background color
		game.setBackgroundColor(0x000000); // Or any other default color
	}
	// Win condition disabled
	// --- Update Digital Clock ---
	if (typeof digitalClockTxt !== 'undefined') {
		var now = new Date();
		digitalClockTxt.setText(formatTime(now));
	}
	// Display laser flashing effect on the second effect button's frame based on mode
	if (effectButtons[1]) {
		if (laserShowMode === 0) {
			// Off mode - normal white tint
			effectButtons[1].tint = 0xFFFFFF;
		} else if (laserShowMode === 1) {
			// Slow mode - slow blue pulsing
			var time = Date.now() * 0.0008; // Slow pulsing
			var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
			var blueValue = Math.floor(intensity * 255);
			effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue;
		} else if (laserShowMode === 2) {
			// Fast mode - slower rainbow cycling
			var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002)
			var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
			var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3)
			effectButtons[1].tint = laserColors[colorIndex];
		}
	}
	// Display fire effect flashing on the first effect button's frame based on mode
	if (effectButtons[0]) {
		if (fireEffectMode === 0) {
			// Off mode - normal white tint
			effectButtons[0].tint = 0xFFFFFF;
		} else if (fireEffectMode === 1) {
			// Normal flames mode - orange flashing
			var time = Date.now() * 0.002; // Medium speed flashing
			var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
			var redValue = Math.floor(255);
			var greenValue = Math.floor(intensity * 128 + 64); // Orange tint
			var blueValue = 0;
			effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
		} else if (fireEffectMode === 2) {
			// Big flames mode - red flashing
			var time = Date.now() * 0.003; // Faster flashing
			var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
			var redValue = Math.floor(255);
			var greenValue = Math.floor(intensity * 64); // More red tint
			var blueValue = Math.floor(intensity * 64);
			effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
		}
	}
	// Display RGB flashing effect on the third effect button's frame if active
	if (isRgbFlashingActive && effectButtons[2]) {
		var time = Date.now() * 0.0005; // Time for animation, even slower
		var hue = time * 360 % 360; // Cycle through hues
		var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash
		effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
	} else if (effectButtons[2]) {
		// If not active, set the tint back to white
		effectButtons[2].tint = 0xFFFFFF;
	}
	// --- UV button continuous purple flashing effect ---
	if (effectButtons[4]) {
		if (uvEffectActive) {
			// Animate between indigo and purple
			var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button
			// Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF)
			var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2);
			// Interpolate RGB
			var r = Math.round(0x4B * (1 - t) + 0x80 * t);
			var g = Math.round(0x00 * (1 - t) + 0x00 * t);
			var b = Math.round(0x82 * (1 - t) + 0xFF * t);
			effectButtons[4].tint = r << 16 | g << 8 | b;
		} else {
			effectButtons[4].tint = 0xFFFFFF;
		}
	}
	// Manage gifts effects based on giftsMode
	if (balloons) {
		for (var i = 0; i < balloons.length; i++) {
			balloons[i].update();
			// Only show balloons if giftsMode is 1 (balloons mode)
			balloons[i].visible = giftsMode === 1;
		}
	}
	if (moneyParticles) {
		for (var i = 0; i < moneyParticles.length; i++) {
			moneyParticles[i].update();
			// Only show money particles if giftsMode is 2 (money mode)
			moneyParticles[i].visible = giftsMode === 2;
		}
	}
	// Manage fireworks visibility (fireworks are spread across multiple instances)
	if (fireworksEffect) fireworksEffect.visible = giftsMode === 3;
	if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3;
	if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3;
	if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3;
	// Update fireworks effect animation only if visible
	if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update();
	if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update();
	if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update();
	if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update();
	// Update gifts button visual feedback based on mode
	if (giftsEffectButton) {
		tween.stop(giftsEffectButton, {
			tint: true
		}); // Stop any previous tint tweens
		if (giftsMode === 0) {
			// Off mode - no light (white tint)
			giftsEffectButton.tint = 0xFFFFFF;
		} else if (giftsMode === 1) {
			// Balloons mode - flashing pink
			var time = Date.now() * 0.002; // Medium speed flashing
			var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
			var pinkValue = Math.floor(intensity * 255);
			giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink
		} else if (giftsMode === 2) {
			// Money mode - green color
			giftsEffectButton.tint = 0x00FF00; // Green
		} else if (giftsMode === 3) {
			// Fireworks mode - flashing gold
			var time = Date.now() * 0.002; // Medium speed flashing
			var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
			var goldR = Math.floor(intensity * 255);
			var goldG = Math.floor(intensity * 215);
			var goldB = Math.floor(intensity * 0);
			giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish
		}
	}
};
tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2;
tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20 - 200 - 35;
// --- Arrow Buttons under Tablet ---
var arrowButtonSpacing = 150;
var arrowButtonY = tabletAsset.y + tabletAsset.height / 2 + 80;
var leftArrowButton = LK.getAsset('Leftarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1;
leftArrowButton.y = arrowButtonY;
game.addChild(leftArrowButton);
// Scale up left arrow button by 1.3
tween(leftArrowButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
var upArrowButton = LK.getAsset('UParrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1;
upArrowButton.y = arrowButtonY;
game.addChild(upArrowButton);
// Scale up up arrow button by 1.3
tween(upArrowButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
var downArrowButton = LK.getAsset('Downarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
downArrowButton.y = arrowButtonY;
game.addChild(downArrowButton);
// Scale up down arrow button by 1.3
tween(downArrowButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
var rightArrowButton = LK.getAsset('Rightarrow', {
	anchorX: 0.5,
	anchorY: 0.5
});
rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
rightArrowButton.y = arrowButtonY;
game.addChild(rightArrowButton);
// Scale up right arrow button by 1.3
tween(rightArrowButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
var okButton = LK.getAsset('OKbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
// Place OK button to the right of the right arrow button with the same spacing as between arrow buttons
okButton.x = upArrowButton.x + arrowButtonSpacing - 12 - 5 - 1 + 2 + 2;
okButton.y = arrowButtonY;
game.addChild(okButton);
// Scale up OK button by 1.3 to match arrow buttons
tween(okButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
// Add edit button under the OK button
var editButton = LK.getAsset('editbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
editButton.x = okButton.x;
editButton.y = okButton.y + okButton.height + 20; // Position 20 units under the OK button
game.addChild(editButton);
// Scale up edit button by 1.3 to match arrow buttons
tween(editButton, {
	scaleX: 1.3,
	scaleY: 1.3
}, {
	duration: 300,
	easing: tween.easeOut
});
// --- Pulse Button Effect State ---
var pulseButtonEffect = 1.0; // 1.0 = normal, <1.0 = decreased
// Helper to decrease pulse effect
function decreasePulseButtonEffect() {
	pulseButtonEffect = 0.5;
	// Optionally, restore after a short time for visual feedback
	LK.setTimeout(function () {
		pulseButtonEffect = 1.0;
	}, 200);
}
// --- Add decrease pulse effect to arrow and OK buttons ---
leftArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateLeft();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
leftArrowButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Navigate left in rekordbox app
	if (typeof rekordboxApp !== 'undefined') {
		// Check if the fullscreen tracklist is currently visible
		if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
			rekordboxApp.navigateLeft();
		}
	}
	// Visual feedback
	tween(leftArrowButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(leftArrowButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
leftArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateLeft();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
rightArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateRight();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
rightArrowButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Navigate right in rekordbox app
	if (typeof rekordboxApp !== 'undefined') {
		rekordboxApp.navigateRight();
	}
	tween(rightArrowButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(rightArrowButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
rightArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateRight();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
upArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateUp();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
upArrowButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Navigate up in rekordbox app
	if (typeof rekordboxApp !== 'undefined') {
		rekordboxApp.navigateUp();
	}
	tween(upArrowButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(upArrowButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
upArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateUp();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
downArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateDown();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
downArrowButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Navigate down in rekordbox app
	if (typeof rekordboxApp !== 'undefined') {
		rekordboxApp.navigateDown();
	}
	tween(downArrowButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(downArrowButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
downArrowButtonKeyboard.down = function (x, y, obj) {
	// Only navigate if the fullscreen tracklist is visible
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible) {
		rekordboxApp.navigateDown();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
okButtonKeyboard.down = function (x, y, obj) {
	// Only select if the fullscreen tracklist is visible and has a selectTrack method
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible && rekordboxApp.selectTrack) {
		rekordboxApp.selectTrack();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
okButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Select track in rekordbox app
	if (typeof rekordboxApp !== 'undefined' && rekordboxApp.selectTrack) {
		rekordboxApp.selectTrack();
	}
	tween(okButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(okButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
okButtonKeyboard.down = function (x, y, obj) {
	// Only select if the fullscreen tracklist is visible and has a selectTrack method
	if (game.children.indexOf(rekordboxApp) !== -1 && rekordboxApp.visible && rekordboxApp.selectTrack) {
		rekordboxApp.selectTrack();
	}
	tween(this, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(this, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 120
			});
		}.bind(this)
	});
};
// Add functionality to edit button
editButton.down = function (x, y, obj) {
	decreasePulseButtonEffect();
	// Create fullscreen song maker app
	var songMakerApp = new SongMakerApp();
	game.addChild(songMakerApp);
	// Create semi-transparent background overlay
	var overlay = LK.getAsset('crossfaderTrack', {
		anchorX: 0,
		anchorY: 0,
		width: 2048,
		height: 2732
	});
	overlay.tint = 0x000000;
	overlay.alpha = 0.7;
	overlay.x = 0;
	overlay.y = 0;
	// Add overlay behind the app
	game.addChild(overlay);
	game.setChildIndex(overlay, game.children.indexOf(songMakerApp) - 1);
	// Add X (close window) button to the full screen song maker app top right corner
	var closeButton = LK.getAsset('X', {
		anchorX: 1.0,
		anchorY: 0.0
	});
	closeButton.scaleX = 2.0;
	closeButton.scaleY = 2.0;
	closeButton.x = 2048 - 20; // 20px from right edge
	closeButton.y = 20; // 20px from top edge
	game.addChild(closeButton);
	// Add close functionality to the X button
	closeButton.down = function (x, y, obj) {
		// Remove fullscreen song maker app, overlay, and close button
		if (game.children.indexOf(songMakerApp) !== -1) {
			game.removeChild(songMakerApp);
		}
		if (game.children.indexOf(overlay) !== -1) {
			game.removeChild(overlay);
		}
		if (game.children.indexOf(closeButton) !== -1) {
			game.removeChild(closeButton);
		}
	};
	// Add close functionality when clicking overlay
	overlay.down = function (x, y, obj) {
		// Remove fullscreen song maker app and overlay
		if (game.children.indexOf(songMakerApp) !== -1) {
			game.removeChild(songMakerApp);
		}
		if (game.children.indexOf(overlay) !== -1) {
			game.removeChild(overlay);
		}
		if (game.children.indexOf(closeButton) !== -1) {
			game.removeChild(closeButton);
		}
	};
	tween(editButton, {
		scaleX: 1.1,
		scaleY: 1.1
	}, {
		duration: 80,
		onFinish: function onFinish() {
			tween(editButton, {
				scaleX: 1.3,
				scaleY: 1.3
			}, {
				duration: 120
			});
		}
	});
};
// Update second dancing people line animations (second set created) - swap with UV assets when UV is active
for (var i = 8; i < dancingPeople.length; i++) {
	var person = dancingPeople[i];
	// Check if UV effect is active
	if (uvEffectActive) {
		// If not already a UV person, swap to UV asset
		if (!person.isUV) {
			var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM';
			var currentScaleX = person.scale.x;
			var currentScaleY = person.scale.y;
			var currentRotation = person.rotation;
			var currentAlpha = person.alpha;
			var currentX = person.x;
			var currentY = person.y;
			var currentBaseY = person.baseY;
			var currentPhaseOffset = person.phaseOffset;
			// Remove the old graphic child
			if (person.children.length > 0) {
				person.removeChildAt(0);
			}
			// Add the new UV graphic child
			var uvGraphic = person.attachAsset(uvAssetId, {
				anchorX: 0.5,
				anchorY: 1.0
			});
			// Restore previous transform and animation properties
			person.scale.set(currentScaleX, currentScaleY);
			person.rotation = currentRotation;
			person.alpha = currentAlpha;
			person.x = currentX;
			person.y = currentY;
			person.baseY = currentBaseY;
			person.phaseOffset = currentPhaseOffset;
			person.isUV = true;
		}
	} else {
		// If UV effect is off and it's a UV person, swap back to normal asset
		if (person.isUV) {
			var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan';
			var currentScaleX = person.scale.x;
			var currentScaleY = person.scale.y;
			var currentRotation = person.rotation;
			var currentAlpha = person.alpha;
			var currentX = person.x;
			var currentY = person.y;
			var currentBaseY = person.baseY;
			var currentPhaseOffset = person.phaseOffset;
			// Remove the old graphic child
			if (person.children.length > 0) {
				person.removeChildAt(0);
			}
			// Add the new normal graphic child
			var normalGraphic = person.attachAsset(normalAssetId, {
				anchorX: 0.5,
				anchorY: 1.0
			});
			// Restore previous transform and animation properties
			person.scale.set(currentScaleX, currentScaleY);
			person.rotation = currentRotation;
			person.alpha = currentAlpha;
			person.x = currentX;
			person.y = currentY;
			person.baseY = currentBaseY;
			person.phaseOffset = currentPhaseOffset;
			person.isUV = false;
		}
	}
	person.update();
}
// Animate extra white particles (subtle sparkle and movement)
if (extraWhiteParticles) {
	var t = Date.now() * 0.001;
	for (var i = 0; i < extraWhiteParticles.length; i++) {
		var p = extraWhiteParticles[i];
		// Subtle float and twinkle
		p.obj.x = p.baseX + Math.sin(t * 1.2 + p.phase + i) * 8;
		p.obj.y = p.baseY + Math.cos(t * 1.1 + p.phase + i * 0.7) * 6;
		p.obj.alpha = p.baseAlpha + 0.10 * Math.sin(t * 2.2 + p.phase + i * 0.5);
		var s = p.baseSize * (0.95 + 0.12 * Math.sin(t * 1.7 + p.phase + i));
		p.obj.width = s;
		p.obj.height = s;
	}
}
// Animate flashing white reflectors
if (reflectors) {
	var t = Date.now() * 0.001;
	for (var i = 0; i < reflectors.length; i++) {
		// Staggered flash, each reflector flashes in sequence
		var phase = t * 2.2 + i * 0.5;
		// Use a sharp pulse for flash, then fade out
		var flash = Math.max(0, Math.sin(phase));
		// Sharpen the flash curve for a strobe effect
		flash = Math.pow(flash, 6);
		reflectors[i].alpha = 0.18 + 0.82 * flash;
		// Animate as perfect circles for extra pop (half size base)
		var size = 60 + 20 * flash;
		// Prevent reflectors from covering the discoball
		// Compute distance from reflector to discoball center
		var dx = reflectors[i].x - discoball.x;
		var dy = reflectors[i].y - discoball.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		// Discoball's visible radius (scaled)
		var discoballRadius = 150 * discoball.scaleX; // matches Discoball class
		// If reflector would overlap discoball, shrink it so it cannot cover
		if (dist < discoballRadius + size / 2) {
			// Max allowed size so edge of reflector does not enter discoball
			var maxSize = Math.max(0, 2 * (dist - discoballRadius));
			if (size > maxSize) size = maxSize;
			if (size < 0) size = 0;
		}
		reflectors[i].width = size;
		reflectors[i].height = size;
	}
}
// Update laser show animation
laserShow.update();
// Update fireworks effect animation
fireworksEffect.update();
fireworksEffect2.update();
fireworksEffect3.update();
fireworksEffect4.update();
// Update Ferris wheel animation
if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) {
	// Defensive check
	ferrisWheel.update();
}
// Update smoke diffuser animation
// --- Synchronize all smoke diffusers so they emit smoke puffs at the same time ---
if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) {
	// Use the first smoke diffuser as the "master" for timing
	var now = Date.now();
	// All diffusers share the same "now" and will create puffs in sync
	for (var i = 0; i < smokeDiffusers.length; i++) {
		var sd = smokeDiffusers[i];
		// Patch: override the update method to use the same "now" for all
		if (sd && typeof sd.update === 'function') {
			// Save original Date.now
			var origDateNow = Date.now;
			Date.now = function () {
				return now;
			};
			sd.update();
			Date.now = origDateNow;
		}
	}
}
// Update flamethrower animation
if (typeof flamethrower !== 'undefined' && flamethrower.update) {
	flamethrower.update();
}
// Update decks
leftDeck.update();
rightDeck.update();
// Update equalizers
leftEqualizer.update();
rightEqualizer.update();
// Simulate beat
var now = Date.now();
beatTimer += now - lastTickTime;
lastTickTime = now;
if (beatTimer >= beatInterval) {
	beatTimer -= beatInterval;
	leftDeck.flashBeat();
	rightDeck.flashBeat();
	LK.getSound('beat').play();
	// Energy drops if not scratching or mixing
	if (!leftDeck.isScratching && !rightDeck.isScratching && crossfader.value > 0.2 && crossfader.value < 0.8) {
		energy -= 2;
		combo = 0;
	} else {
		// Combo up if scratching or crossfading
		combo += 1;
		score += 10 * combo;
		energy += 2;
		if (energy > 100) energy = 100;
	}
	if (energy < 0) energy = 0;
	// Update visuals
	scoreTxt.setText('Score: ' + score);
	if (combo > 1) {
		comboTxt.setText('Combo x' + combo);
	} else {
		comboTxt.setText('');
	}
}
// Update energy bar
energyBar.width = 600 * (energy / 100);
// Crossfader logic: adjust music volumes
trackAVol = 1 - crossfader.value;
trackBVol = crossfader.value;
// (In a real game, would set music volumes here, but LK handles music globally.)
// End game if energy is 0
if (energy <= 0) {
	// Game over event disabled
}
// Animate background color
var time = Date.now() * 0.0005; // Time for animation
var hue = time * 360 % 360; // Cycle through hues over time
var rgb = hsvToRgb(hue / 360, 0.6, 0.5); // Convert HSV to RGB (adjust saturation and value for desired effect)
// Only animate the background color if RGB flashing is active on the third effect button
if (isRgbFlashingActive) {
	game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]);
} else if (uvEffectActive) {
	// If UV effect is active, flash between indigo blue and purple
	var uvTime = Date.now() * 0.002; // Faster flashing for UV
	var uvHue = uvTime * 360 % 360;
	// Cycle between indigo (around 240 hue) and purple (around 270 hue)
	var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30;
	targetHue = (targetHue + 360) % 360;
	var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors
	game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]);
} else {
	// If neither is active, set a default background color
	game.setBackgroundColor(0x000000); // Or any other default color
}
// Win condition disabled
// --- Update Digital Clock ---
if (typeof digitalClockTxt !== 'undefined') {
	var now = new Date();
	digitalClockTxt.setText(formatTime(now));
}
// Display laser flashing effect on the second effect button's frame based on mode
if (effectButtons[1]) {
	if (laserShowMode === 0) {
		// Off mode - normal white tint
		effectButtons[1].tint = 0xFFFFFF;
	} else if (laserShowMode === 1) {
		// Slow mode - slow blue pulsing
		var time = Date.now() * 0.0008; // Slow pulsing
		var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
		var blueValue = Math.floor(intensity * 255);
		effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue;
	} else if (laserShowMode === 2) {
		// Fast mode - slower rainbow cycling
		var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002)
		var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
		var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3)
		effectButtons[1].tint = laserColors[colorIndex];
	}
}
// Display fire effect flashing on the first effect button's frame based on mode
if (effectButtons[0]) {
	if (fireEffectMode === 0) {
		// Off mode - normal white tint
		effectButtons[0].tint = 0xFFFFFF;
	} else if (fireEffectMode === 1) {
		// Normal flames mode - orange flashing
		var time = Date.now() * 0.002; // Medium speed flashing
		var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
		var redValue = Math.floor(255);
		var greenValue = Math.floor(intensity * 128 + 64); // Orange tint
		var blueValue = 0;
		effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
	} else if (fireEffectMode === 2) {
		// Big flames mode - red flashing
		var time = Date.now() * 0.003; // Faster flashing
		var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
		var redValue = Math.floor(255);
		var greenValue = Math.floor(intensity * 64); // More red tint
		var blueValue = Math.floor(intensity * 64);
		effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
	}
}
// Display RGB flashing effect on the third effect button's frame if active
if (isRgbFlashingActive && effectButtons[2]) {
	var time = Date.now() * 0.0005; // Time for animation, even slower
	var hue = time * 360 % 360; // Cycle through hues
	var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash
	effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
} else if (effectButtons[2]) {
	// If not active, set the tint back to white
	effectButtons[2].tint = 0xFFFFFF;
}
// --- UV button continuous purple flashing effect ---
if (effectButtons[4]) {
	if (uvEffectActive) {
		// Animate between indigo and purple
		var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button
		// Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF)
		var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2);
		// Interpolate RGB
		var r = Math.round(0x4B * (1 - t) + 0x80 * t);
		var g = Math.round(0x00 * (1 - t) + 0x00 * t);
		var b = Math.round(0x82 * (1 - t) + 0xFF * t);
		effectButtons[4].tint = r << 16 | g << 8 | b;
	} else {
		effectButtons[4].tint = 0xFFFFFF;
	}
}
// Manage gifts effects based on giftsMode
if (balloons) {
	for (var i = 0; i < balloons.length; i++) {
		balloons[i].update();
		// Only show balloons if giftsMode is 1 (balloons mode)
		balloons[i].visible = giftsMode === 1;
	}
}
if (moneyParticles) {
	for (var i = 0; i < moneyParticles.length; i++) {
		moneyParticles[i].update();
		// Only show money particles if giftsMode is 2 (money mode)
		moneyParticles[i].visible = giftsMode === 2;
	}
}
// Manage fireworks visibility (fireworks are spread across multiple instances)
if (fireworksEffect) fireworksEffect.visible = giftsMode === 3;
if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3;
if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3;
if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3;
// Update fireworks effect animation only if visible
if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update();
if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update();
if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update();
if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update();
// Update gifts button visual feedback based on mode
if (giftsEffectButton) {
	tween.stop(giftsEffectButton, {
		tint: true
	}); // Stop any previous tint tweens
	if (giftsMode === 0) {
		// Off mode - no light (white tint)
		giftsEffectButton.tint = 0xFFFFFF;
	} else if (giftsMode === 1) {
		// Balloons mode - flashing pink
		var time = Date.now() * 0.002; // Medium speed flashing
		var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
		var pinkValue = Math.floor(intensity * 255);
		giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink
	} else if (giftsMode === 2) {
		// Money mode - green color
		giftsEffectButton.tint = 0x00FF00; // Green
	} else if (giftsMode === 3) {
		// Fireworks mode - flashing gold
		var time = Date.now() * 0.002; // Medium speed flashing
		var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
		var goldR = Math.floor(intensity * 255);
		var goldG = Math.floor(intensity * 215);
		var goldB = Math.floor(intensity * 0);
		giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish
	}
}
okButton.y = arrowButtonY + 12 + 30 - 25; // Move OK button down by 30 units
game.addChild(okButton);
leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1;
leftArrowButton.y = arrowButtonY + 12 + 30 - 25; // Move left arrow down by 30 units
game.addChild(leftArrowButton);
upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1;
upArrowButton.y = arrowButtonY + 12 + 30 - 25; // Move up arrow down by 30 units
game.addChild(upArrowButton);
downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
downArrowButton.y = arrowButtonY + 12 + 30 - 25; // Move down arrow down by 30 units
game.addChild(downArrowButton);
rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
rightArrowButton.y = arrowButtonY + 12 + 30 - 25; // Move right arrow down by 30 units
game.addChild(rightArrowButton);
// Update edit button position in update loop
if (typeof editButton !== 'undefined' && typeof okButton !== 'undefined') {
	editButton.x = okButton.x;
	editButton.y = okButton.y + okButton.height + 20 + 12 - 25 + 20 + 20; // Move down by 12 units + 20 units
}
var editButton = LK.getAsset('editbutton', {
	anchorX: 0.5,
	anchorY: 0.5
});
fxButton.y = fxButtonY + 200 + 10;
// Add white A letter under fx button
var fxButtonAText = new Text2('A', {
	size: 48,
	fill: 0xFFFFFF // White color
});
fxButtonAText.anchor.set(0.5, 0.5);
fxButtonAText.x = fxButton.x - 150;
fxButtonAText.y = fxButton.y + fxButton.height / 2 + 20 + 90 + 12; // Position under the button, moved down by 90 units, then down by 12 units
game.addChild(fxButtonAText);
// Add functionality to A button text to toggle trackA music
fxButtonAText.down = function (x, y, obj) {
	// Toggle trackA playing state
	trackAPlaying = !trackAPlaying;
	if (trackAPlaying) {
		// Start playing trackA
		LK.playMusic('trackA', {
			loop: true
		});
		// Visual feedback - green color when playing
		tween(fxButtonAText, {
			tint: 0x00ff00
		}, {
			duration: 200
		});
	} else {
		// Stop playing trackA
		LK.stopMusic();
		// Visual feedback - return to white when stopped
		tween(fxButtonAText, {
			tint: 0xffffff
		}, {
			duration: 200
		});
	}
	// Visual feedback - scale animation
	tween(fxButtonAText, {
		scaleX: 1.2,
		scaleY: 1.2
	}, {
		duration: 100,
		onFinish: function onFinish() {
			tween(fxButtonAText, {
				scaleX: 1.0,
				scaleY: 1.0
			}, {
				duration: 100
			});
		}
	});
	// Add score bonus
	score += 50;
	combo += 1;
};
leftFilterKnobMid.y = leftDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12;
// Add white MID text under the MID filter knob
var midTextLeft = new Text2('MID', {
	size: 24,
	fill: 0xFFFFFF // White color
});
midTextLeft.anchor.set(0.5, 0.5);
midTextLeft.x = leftFilterKnobMid.x;
midTextLeft.y = leftFilterKnobMid.y + leftFilterKnobMid.height / 2 + 20; // Position under the knob
game.addChild(midTextLeft);
tween(leftFilterKnobMid, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var leftFilterKnobLow = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
rightFilterKnobMid.y = rightDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12;
// Add white MID text under the MID filter knob (Right side)
var midTextRight = new Text2('MID', {
	size: 24,
	fill: 0xFFFFFF // White color
});
midTextRight.anchor.set(0.5, 0.5);
midTextRight.x = rightFilterKnobMid.x;
midTextRight.y = rightFilterKnobMid.y + rightFilterKnobMid.height / 2 + 20; // Position under the knob
game.addChild(midTextRight);
tween(rightFilterKnobMid, {
	scaleX: 1.2,
	scaleY: 1.2
}, {
	duration: 300,
	easing: tween.easeOut
});
var rightFilterKnobLow = LK.getAsset('filterKnob', {
	anchorX: 0.5,
	anchorY: 0.5
});
logoAsset.x = crossfader.x;
logoAsset.y = crossfader.y + crossfader.height / 2 + 30 - 350 - 40; // Position under the crossfader, moved up by 350 units;
logoAsset.x = crossfader.x;
logoAsset.y = crossfader.y + crossfader.height / 2 + 30 - 350 - 40 - 50; // Position under the crossfader, moved up by 350 units;;
masterButtonBText.x = masterAsset.x + 150;
masterButtonBText.y = masterAsset.y + masterAsset.height / 2 + 20 + 100; // Position under the button, moved down by 100 units
masterButtonBText.y = masterAsset.y + masterAsset.height / 2 + 20 + 100 + 10; // Position under the button, moved down by 100 units, moved down by 10 units;
game.addChild(masterButtonBText);
// Add rec button asset in the middle of the screen ===================================================================
--- original.js
+++ change.js
@@ -4442,8 +4442,9 @@
 	return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
 }
 var trackAVol = 1;
 var trackBVol = 1;
+var trackAPlaying = false; // Track whether trackA is currently playing
 // --- Touch handling ---
 function getTouchedControl(x, y) {
 	// Check decks
 	var lx = leftDeck.toLocal(game.toGlobal({
@@ -4810,21 +4811,15 @@
 				}
 				createPulse();
 			}
 		});
-		// Start playing trackA music when button is on
-		LK.playMusic('trackA', {
-			loop: true
-		});
 	} else {
 		// Turn off light effect - return to normal tint
 		tween(leftStartButton, {
 			tint: 0xffffff
 		}, {
 			duration: 200
 		});
-		// Stop playing trackA music when button is off
-		LK.stopMusic();
 	}
 	// Start/stop left deck
 	leftDeck.playing = !leftDeck.playing;
 	// Start/stop left deck spinning
@@ -7702,8 +7697,52 @@
 fxButtonAText.anchor.set(0.5, 0.5);
 fxButtonAText.x = fxButton.x - 150;
 fxButtonAText.y = fxButton.y + fxButton.height / 2 + 20 + 90 + 12; // Position under the button, moved down by 90 units, then down by 12 units
 game.addChild(fxButtonAText);
+// Add functionality to A button text to toggle trackA music
+fxButtonAText.down = function (x, y, obj) {
+	// Toggle trackA playing state
+	trackAPlaying = !trackAPlaying;
+	if (trackAPlaying) {
+		// Start playing trackA
+		LK.playMusic('trackA', {
+			loop: true
+		});
+		// Visual feedback - green color when playing
+		tween(fxButtonAText, {
+			tint: 0x00ff00
+		}, {
+			duration: 200
+		});
+	} else {
+		// Stop playing trackA
+		LK.stopMusic();
+		// Visual feedback - return to white when stopped
+		tween(fxButtonAText, {
+			tint: 0xffffff
+		}, {
+			duration: 200
+		});
+	}
+	// Visual feedback - scale animation
+	tween(fxButtonAText, {
+		scaleX: 1.2,
+		scaleY: 1.2
+	}, {
+		duration: 100,
+		onFinish: function onFinish() {
+			tween(fxButtonAText, {
+				scaleX: 1.0,
+				scaleY: 1.0
+			}, {
+				duration: 100
+			});
+		}
+	});
+	// Add score bonus
+	score += 50;
+	combo += 1;
+};
 leftFilterKnobMid.y = leftDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12;
 // Add white MID text under the MID filter knob
 var midTextLeft = new Text2('MID', {
 	size: 24,
 
 
 Photorealistic Ferris cabin, sideview
 
 
 
 
 
 Grey circle with transparent inner place, front view
 there are 4 piece of holes in the image, repair it
 
 
 
 photorealistic grey Tablet lying down on dj deck, front view. The screen should be dark monocrome screen in power of mode without text.
 
 
 Photorealistic transparent rectange dj deck sample button with white rounded corners, front view. No text needed on image
 Transparent ballon shape.
 
 Replace arrow to OK text
 Dancing woman in a white top filled with multi-colored UV ink palm prints. UV luminous bracelet on the arm
 
 Photorealistic dollar, front view
 Dancing man in a white shirt filled with multi-colored UV ink palm prints. UV luminous bracelet on the arm
 
 White thin pencil in 45 degrees instead of arrow.
 Replace arrow to white X
 Change the orange filling color from the middle to black
 
 simple grey colored DJZ logo
 REPLACE THE PENCIL WITH WHITE MENU TEXT
 Replace pencil with white REC text, but no other changes!
1
Music
trackA
Music
trackB
Music
sample1
Sound effect
sample2
Sound effect
sample3
Sound effect
sample4
Sound effect
Sample5
Sound effect
sample6
Sound effect
sample7
Sound effect
sample8
Sound effect
sample9
Sound effect
sample10
Sound effect
sample11
Sound effect
sample12
Sound effect
sample13
Sound effect
sample14
Sound effect
sample15
Sound effect
sample16
Sound effect