User prompt
biraz daha az rastgele melodi üret müzik karmaşası olmasın biraz daha düzgün ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
kafasını sağ sola oynatıncada farklı şeyler yap gözlerini kapatıp açınca ağzını burnunu oynatınca ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
kick snare hihat filter-sweep delay-effect reverb-wash gibi ses efektleri ekledim bunlarıda uygunluğa göre kullan ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Build a music-based interactive game titled "FaceToNote". When the player starts the game, activate the webcam and scan the player’s face using FaceKit. Analyze key facial features such as eyebrow angle, eye distance, mouth width, chin length, and head tilt. Convert these features into abstract shapes (circle, arc, triangle, square) on screen. Then, map each shape to a musical role: Arcs → melody (e.g. piano notes: G3, B3, C4) Ellipses → bass (e.g. synth bass E2) Squares → rhythm (kick/snare/hi-hat) Lines or dots → sound effects (filter sweep, delay, reverb) Head tilt → stereo pan (left or right) After scanning, generate a looping sequence where each shape plays its assigned sound. The result is a personalized music composition based on the player's face. During gameplay, detect real-time expressions: Smiling adds new melody notes Eyebrow frown increases BPM Blinking triggers random effects Visually, show the scanned face with stylized scanning effects (e.g., wireframe mesh, glowing shapes) and sync visual pulses to the beat. Keep the UI minimal, ambient, and responsive to audio. ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
assets içerisinde bir sürü nota ses efekti ekledim kişinin yüz şekline göre rastgele notaları çal ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
okey add background or black color
Code edit (1 edits merged)
Please save this source code
User prompt
Face Scan Shape Creator
Initial prompt
scan the face and create face shape
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var ExpressionDetector = Container.expand(function () { var self = Container.call(this); // Expression state tracking self.lastSmiling = false; self.lastEyebrowsFrowning = false; self.lastBlinking = false; self.blinkCounter = 0; self.lastBlinkTime = 0; // Head movement tracking self.lastHeadX = 0; self.lastHeadY = 0; self.lastEyesOpen = true; self.lastMouthPosition = 0; self.lastNosePosition = 0; // Expression thresholds self.smileThreshold = 0.3; self.frownThreshold = 0.2; self.blinkThreshold = 50; // milliseconds self.headMovementThreshold = 30; self.eyeCloseThreshold = 20; self.mouthMovementThreshold = 15; self.noseMovementThreshold = 10; self.detectSmile = function () { if (!facekit.upperLip || !facekit.lowerLip) return false; // Simple smile detection based on mouth corner positions var mouthCurve = (facekit.upperLip.y - facekit.lowerLip.y) / 20; return mouthCurve > self.smileThreshold; }; self.detectFrown = function () { if (!facekit.leftEye || !facekit.rightEye) return false; // Detect frown based on eyebrow position relative to eyes var eyebrowPosition = (facekit.leftEye.y + facekit.rightEye.y) / 2 - 30; return eyebrowPosition < self.frownThreshold; }; self.detectBlink = function () { // Simplified blink detection (in real implementation would track eye closure) var currentTime = Date.now(); if (currentTime - self.lastBlinkTime > 2000) { // Auto-trigger every 2 seconds for demo self.lastBlinkTime = currentTime; return true; } return false; }; self.detectHeadMovement = function () { if (!facekit.mouthCenter) return { left: false, right: false, up: false, down: false }; var currentHeadX = facekit.mouthCenter.x; var currentHeadY = facekit.mouthCenter.y; var deltaX = currentHeadX - self.lastHeadX; var deltaY = currentHeadY - self.lastHeadY; return { left: deltaX < -self.headMovementThreshold, right: deltaX > self.headMovementThreshold, up: deltaY < -self.headMovementThreshold, down: deltaY > self.headMovementThreshold }; }; self.detectEyeMovement = function () { if (!facekit.leftEye || !facekit.rightEye) return false; // Detect eye closing based on eye position change var eyeHeight = Math.abs(facekit.leftEye.y - facekit.rightEye.y); var eyesCurrentlyOpen = eyeHeight > self.eyeCloseThreshold; return eyesCurrentlyOpen !== self.lastEyesOpen; }; self.detectMouthMovement = function () { if (!facekit.mouthCenter) return false; var currentMouthY = facekit.mouthCenter.y; var deltaY = Math.abs(currentMouthY - self.lastMouthPosition); return deltaY > self.mouthMovementThreshold; }; self.detectNoseMovement = function () { if (!facekit.noseTip) return false; var currentNoseY = facekit.noseTip.y; var deltaY = Math.abs(currentNoseY - self.lastNosePosition); return deltaY > self.noseMovementThreshold; }; self.onSmile = function (sequencer) { // Add harmonious melody notes when smiling (reduced randomness) var lastNoteIndex = sequencer.melodySequence.length > 0 ? sequencer.melodySequence[sequencer.melodySequence.length - 1].noteIndex || 0 : 0; var harmonicNoteIndex = (lastNoteIndex + 2) % melodyNotes.length; // Always use harmonic intervals var newMelodyNote = melodyNotes[harmonicNoteIndex]; LK.getSound(newMelodyNote).play(); LK.getSound('smile-trigger').play(); // Visual feedback LK.effects.flashScreen(0x00ff88, 300); // Add to score LK.setScore(LK.getScore() + 25); }; self.onFrown = function (sequencer) { // Increase BPM when frowning sequencer.bpm = Math.min(180, sequencer.bpm + 5); sequencer.beatInterval = 60000 / sequencer.bpm; LK.getSound('frown-trigger').play(); // Visual feedback - red flash LK.effects.flashScreen(0xff4444, 200); }; self.onBlink = function (sequencer) { // Trigger random effects on blink var randomEffect = effectSounds[Math.floor(Math.random() * effectSounds.length)]; LK.getSound(randomEffect).play(); LK.getSound('blink-effect').play(); // Create sparkle effect for (var i = 0; i < 5; i++) { var sparkle = game.addChild(LK.getAsset('effectDot', { anchorX: 0.5, anchorY: 0.5, alpha: 1.0, tint: Math.random() * 0xffffff })); sparkle.x = facekit.mouthCenter.x + (Math.random() - 0.5) * 200; sparkle.y = facekit.mouthCenter.y + (Math.random() - 0.5) * 200; tween(sparkle, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 800, onFinish: function onFinish() { sparkle.destroy(); } }); } }; self.onHeadMovement = function (direction, sequencer) { // Different effects for different head movements if (direction.left) { // Pan music left and play bass note sequencer.panValue = -0.8; LK.getSound('bass-e2').play(); LK.effects.flashScreen(0x3498db, 300); } else if (direction.right) { // Pan music right and play different bass note sequencer.panValue = 0.8; LK.getSound('bass-a2').play(); LK.effects.flashScreen(0xe74c3c, 300); } else if (direction.up) { // Increase BPM and play high melody sequencer.bpm = Math.min(160, sequencer.bpm + 10); sequencer.beatInterval = 60000 / sequencer.bpm; LK.getSound('melody-a4').play(); LK.effects.flashScreen(0xf1c40f, 200); } else if (direction.down) { // Decrease BPM and play low melody sequencer.bpm = Math.max(80, sequencer.bpm - 10); sequencer.beatInterval = 60000 / sequencer.bpm; LK.getSound('melody-g3').play(); LK.effects.flashScreen(0x9b59b6, 200); } LK.setScore(LK.getScore() + 15); }; self.onEyeMovement = function (sequencer) { // Create eye-sparkle effect and play chord var chordNotes = ['melody-c4', 'melody-e4', 'melody-g3']; for (var i = 0; i < chordNotes.length; i++) { LK.setTimeout(function (noteIndex) { return function () { LK.getSound(chordNotes[noteIndex]).play(); }; }(i), i * 100); } // Create eye glow effect for (var i = 0; i < 8; i++) { var glow = game.addChild(LK.getAsset('effectDot', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, tint: 0x00ff88 })); var eyeX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var angle = i / 8 * Math.PI * 2; glow.x = eyeX + Math.cos(angle) * 80; glow.y = eyeY + Math.sin(angle) * 50; tween(glow, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 600, onFinish: function onFinish() { glow.destroy(); } }); } LK.setScore(LK.getScore() + 20); }; self.onMouthMovement = function (sequencer) { // Play vocal-like sounds and create mouth wave effect var vocalNotes = ['do-c', 're-d', 'fa-f', 'sol-g', 'la-a', 'si-b']; var randomVocal = vocalNotes[Math.floor(Math.random() * vocalNotes.length)]; LK.getSound(randomVocal).play(); // Create wave effect from mouth for (var i = 0; i < 6; i++) { var wave = game.addChild(LK.getAsset('melodyArc', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, tint: 0xff6b6b })); wave.x = facekit.mouthCenter.x; wave.y = facekit.mouthCenter.y; wave.scaleX = 0.5; wave.scaleY = 0.5; tween(wave, { scaleX: 2 + i * 0.3, scaleY: 2 + i * 0.3, alpha: 0 }, { duration: 800 + i * 100, onFinish: function onFinish() { wave.destroy(); } }); } LK.setScore(LK.getScore() + 12); }; self.onNoseMovement = function (sequencer) { // Play filter effect and create nose particle burst LK.getSound('filter-sweep').play(); // Create particle burst from nose for (var i = 0; i < 10; i++) { var particle = game.addChild(LK.getAsset('effectDot', { anchorX: 0.5, anchorY: 0.5, alpha: 1.0, tint: 0xf39c12 })); particle.x = facekit.noseTip.x; particle.y = facekit.noseTip.y; var angle = Math.random() * Math.PI * 2; var speed = 3 + Math.random() * 4; var targetX = particle.x + Math.cos(angle) * speed * 30; var targetY = particle.y + Math.sin(angle) * speed * 30; tween(particle, { x: targetX, y: targetY, scaleX: 0.2, scaleY: 0.2, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { particle.destroy(); } }); } LK.setScore(LK.getScore() + 8); }; self.update = function (sequencer) { // Check for expression changes var currentSmiling = self.detectSmile(); var currentFrowning = self.detectFrown(); var currentBlinking = self.detectBlink(); var headMovement = self.detectHeadMovement(); var eyeMovement = self.detectEyeMovement(); var mouthMovement = self.detectMouthMovement(); var noseMovement = self.detectNoseMovement(); // Trigger events on state transitions if (!self.lastSmiling && currentSmiling) { self.onSmile(sequencer); } if (!self.lastEyebrowsFrowning && currentFrowning) { self.onFrown(sequencer); } if (currentBlinking) { self.onBlink(sequencer); } // New movement detection if (headMovement.left || headMovement.right || headMovement.up || headMovement.down) { self.onHeadMovement(headMovement, sequencer); } if (eyeMovement) { self.onEyeMovement(sequencer); } if (mouthMovement) { self.onMouthMovement(sequencer); } if (noseMovement) { self.onNoseMovement(sequencer); } // Update last states self.lastSmiling = currentSmiling; self.lastEyebrowsFrowning = currentFrowning; // Update position tracking if (facekit.mouthCenter) { self.lastHeadX = facekit.mouthCenter.x; self.lastHeadY = facekit.mouthCenter.y; } if (facekit.leftEye && facekit.rightEye) { var eyeHeight = Math.abs(facekit.leftEye.y - facekit.rightEye.y); self.lastEyesOpen = eyeHeight > self.eyeCloseThreshold; } if (facekit.mouthCenter) { self.lastMouthPosition = facekit.mouthCenter.y; } if (facekit.noseTip) { self.lastNosePosition = facekit.noseTip.y; } }; return self; }); var FaceShape = Container.expand(function () { var self = Container.call(this); self.faceOutline = self.attachAsset('faceOutline', { anchorX: 0.5, anchorY: 0.5 }); self.leftEye = self.attachAsset('leftEye', { anchorX: 0.5, anchorY: 0.5 }); self.rightEye = self.attachAsset('rightEye', { anchorX: 0.5, anchorY: 0.5 }); self.nose = self.attachAsset('nose', { anchorX: 0.5, anchorY: 0.5 }); self.mouth = self.attachAsset('mouth', { anchorX: 0.5, anchorY: 0.5 }); self.currentColorIndex = 0; self.colorPalettes = [[0x3498db, 0xe74c3c, 0xf39c12, 0x9b59b6], [0x2ecc71, 0xe67e22, 0x34495e, 0x1abc9c], [0xf1c40f, 0x8e44ad, 0x16a085, 0xc0392b], [0x95a5a6, 0x2980b9, 0x27ae60, 0xd35400]]; self.lastMouthOpen = false; self.updateFacePosition = function (faceX, faceY, faceWidth, faceHeight) { self.x = faceX; self.y = faceY; // Scale based on face size var scale = Math.max(faceWidth / 300, faceHeight / 400) * 0.8; self.faceOutline.scaleX = scale; self.faceOutline.scaleY = scale; // Position facial features relative to face center self.leftEye.x = -faceWidth * 0.15; self.leftEye.y = -faceHeight * 0.1; self.rightEye.x = faceWidth * 0.15; self.rightEye.y = -faceHeight * 0.1; self.nose.x = 0; self.nose.y = 0; self.mouth.x = 0; self.mouth.y = faceHeight * 0.15; }; self.changeColors = function () { self.currentColorIndex = (self.currentColorIndex + 1) % self.colorPalettes.length; var palette = self.colorPalettes[self.currentColorIndex]; tween(self.faceOutline, { tint: palette[0] }, { duration: 500 }); tween(self.leftEye, { tint: palette[1] }, { duration: 500 }); tween(self.rightEye, { tint: palette[1] }, { duration: 500 }); tween(self.nose, { tint: palette[2] }, { duration: 500 }); tween(self.mouth, { tint: palette[3] }, { duration: 500 }); LK.getSound('shapeChange').play(); }; self.update = function () { // Check for mouth open expression change var currentMouthOpen = facekit.mouthOpen; if (!self.lastMouthOpen && currentMouthOpen) { // Mouth just opened self.triggerExpression(); } self.lastMouthOpen = currentMouthOpen; // Update mouth shape based on expression if (currentMouthOpen) { self.mouth.scaleY = 1.5; } else { self.mouth.scaleY = 1.0; } }; self.triggerExpression = function () { // Create particle effect createParticleEffect(self.x, self.y); // Pulse effect on face outline tween(self.faceOutline, { scaleX: self.faceOutline.scaleX * 1.2, scaleY: self.faceOutline.scaleY * 1.2 }, { duration: 200, onFinish: function onFinish() { tween(self.faceOutline, { scaleX: self.faceOutline.scaleX / 1.2, scaleY: self.faceOutline.scaleY / 1.2 }, { duration: 200 }); } }); // Play random note based on current face position and expression var randomNoteIndex = Math.floor(Math.random() * noteIds.length); LK.getSound(noteIds[randomNoteIndex]).play(); LK.getSound('expressionTrigger').play(); LK.setScore(LK.getScore() + 10); scoreText.setText('Score: ' + LK.getScore()); }; return self; }); var FacialAnalyzer = Container.expand(function () { var self = Container.call(this); // Musical shape arrays self.melodyArcs = []; self.bassEllipses = []; self.rhythmSquares = []; self.effectDots = []; self.scanLines = []; self.wireframes = []; // Face analysis data self.eyebrowAngle = 0; self.eyeDistance = 0; self.mouthWidth = 0; self.chinLength = 0; self.headTilt = 0; // Musical timing self.bpm = 120; self.beatCount = 0; self.lastBeatTime = 0; self.analyzeFace = function () { if (!facekit.leftEye || !facekit.rightEye || !facekit.mouthCenter) return; // Calculate facial measurements self.eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); self.mouthWidth = Math.abs(facekit.upperLip.x - facekit.lowerLip.x) || 50; self.chinLength = Math.abs(facekit.chin.y - facekit.mouthCenter.y); self.headTilt = (facekit.rightEye.y - facekit.leftEye.y) / self.eyeDistance; // Generate shapes based on facial features self.generateMelodyArcs(); self.generateBassEllipses(); self.generateRhythmSquares(); self.generateEffectDots(); }; self.generateMelodyArcs = function () { // Clear existing arcs for (var i = 0; i < self.melodyArcs.length; i++) { self.melodyArcs[i].destroy(); } self.melodyArcs = []; // Create arcs based on eyebrow angle and eye distance var arcCount = Math.floor(self.eyeDistance / 50) + 2; for (var i = 0; i < arcCount; i++) { var arc = self.addChild(LK.getAsset('melodyArc', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 })); arc.x = facekit.leftEye.x + i * (self.eyeDistance / arcCount); arc.y = facekit.leftEye.y - 50; arc.rotation = self.headTilt * 0.5; arc.noteIndex = i % melodyNotes.length; self.melodyArcs.push(arc); } }; self.generateBassEllipses = function () { // Clear existing ellipses for (var i = 0; i < self.bassEllipses.length; i++) { self.bassEllipses[i].destroy(); } self.bassEllipses = []; // Create bass ellipses based on mouth width var ellipseCount = Math.floor(self.mouthWidth / 30) + 1; for (var i = 0; i < ellipseCount; i++) { var ellipse = self.addChild(LK.getAsset('bassEllipse', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 })); ellipse.x = facekit.mouthCenter.x + (i - ellipseCount / 2) * 60; ellipse.y = facekit.mouthCenter.y + 80; ellipse.noteIndex = i % bassNotes.length; self.bassEllipses.push(ellipse); } }; self.generateRhythmSquares = function () { // Clear existing squares for (var i = 0; i < self.rhythmSquares.length; i++) { self.rhythmSquares[i].destroy(); } self.rhythmSquares = []; // Create rhythm squares based on chin length var squareCount = Math.floor(self.chinLength / 40) + 2; for (var i = 0; i < squareCount; i++) { var square = self.addChild(LK.getAsset('rhythmSquare', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 })); square.x = facekit.chin.x + (i - squareCount / 2) * 70; square.y = facekit.chin.y + 50; square.rhythmIndex = i % rhythmSounds.length; self.rhythmSquares.push(square); } }; self.generateEffectDots = function () { // Clear existing dots for (var i = 0; i < self.effectDots.length; i++) { self.effectDots[i].destroy(); } self.effectDots = []; // Create effect dots around nose area var dotCount = 4; for (var i = 0; i < dotCount; i++) { var dot = self.addChild(LK.getAsset('effectDot', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 })); var angle = i / dotCount * Math.PI * 2; dot.x = facekit.noseTip.x + Math.cos(angle) * 60; dot.y = facekit.noseTip.y + Math.sin(angle) * 40; dot.effectIndex = i % effectSounds.length; self.effectDots.push(dot); } }; self.createScanEffect = function () { // Create scanning wireframe effect var wireframe = self.addChild(LK.getAsset('faceWireframe', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 })); wireframe.x = facekit.mouthCenter.x; wireframe.y = facekit.mouthCenter.y; // Animate scanning effect tween(wireframe, { alpha: 0.8, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, onFinish: function onFinish() { tween(wireframe, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, onFinish: function onFinish() { wireframe.destroy(); } }); } }); }; return self; }); var MusicSequencer = Container.expand(function () { var self = Container.call(this); self.bpm = 120; self.beatInterval = 60000 / self.bpm; // milliseconds per beat self.lastBeatTime = 0; self.currentBeat = 0; self.isPlaying = false; // Musical sequences based on face analysis self.melodySequence = []; self.bassSequence = []; self.rhythmSequence = []; self.effectSequence = []; // Stereo panning based on head tilt self.panValue = 0; self.generateSequence = function (analyzer) { // Generate melody sequence from arcs with reduced randomness self.melodySequence = []; // Create a more structured melody pattern var baseNoteIndex = 0; // Start from root note for (var i = 0; i < analyzer.melodyArcs.length; i++) { var arc = analyzer.melodyArcs[i]; // Use sequential notes with small jumps for better harmony var noteIndex = (baseNoteIndex + i) % melodyNotes.length; // Limit note jumps to create smoother melodies if (i > 0 && Math.abs(noteIndex - self.melodySequence[i - 1].noteIndex) > 2) { noteIndex = (self.melodySequence[i - 1].noteIndex + 1) % melodyNotes.length; } self.melodySequence.push({ note: melodyNotes[noteIndex], noteIndex: noteIndex, beat: i % 4, volume: 0.6 + arc.alpha * 0.4 }); } // Generate bass sequence from ellipses self.bassSequence = []; for (var i = 0; i < analyzer.bassEllipses.length; i++) { var ellipse = analyzer.bassEllipses[i]; self.bassSequence.push({ note: bassNotes[ellipse.noteIndex], beat: i % 2, volume: 0.8 }); } // Generate rhythm sequence from squares self.rhythmSequence = []; for (var i = 0; i < analyzer.rhythmSquares.length; i++) { var square = analyzer.rhythmSquares[i]; self.rhythmSequence.push({ sound: rhythmSounds[square.rhythmIndex], beat: i % 4, volume: 0.7 }); } // Set up effect triggers from dots self.effectSequence = []; for (var i = 0; i < analyzer.effectDots.length; i++) { var dot = analyzer.effectDots[i]; self.effectSequence.push({ effect: effectSounds[dot.effectIndex], trigger: Math.random() < 0.3 }); } self.isPlaying = true; }; self.updatePanning = function (headTilt) { self.panValue = Math.max(-1, Math.min(1, headTilt * 2)); // Apply panning to current sounds (simplified representation) }; self.playBeat = function () { if (!self.isPlaying) return; var beatInSequence = self.currentBeat % 4; // Play melody with reduced frequency for cleaner sound var melodyCount = 0; for (var i = 0; i < self.melodySequence.length; i++) { var melodyNote = self.melodySequence[i]; if (melodyNote.beat === beatInSequence) { // Only play every other melody note to reduce clutter if (melodyCount % 2 === 0 || beatInSequence === 0) { LK.getSound(melodyNote.note).play(); } melodyCount++; } } // Play bass for (var i = 0; i < self.bassSequence.length; i++) { var bassNote = self.bassSequence[i]; if (bassNote.beat === beatInSequence) { LK.getSound(bassNote.note).play(); } } // Play rhythm for (var i = 0; i < self.rhythmSequence.length; i++) { var rhythmHit = self.rhythmSequence[i]; if (rhythmHit.beat === beatInSequence) { LK.getSound(rhythmHit.sound).play(); } } // Reduced random effect triggers for cleaner composition if (Math.random() < 0.03 && beatInSequence === 0) { // Only on beat 0, much less frequent var randomEffect = self.effectSequence[Math.floor(Math.random() * self.effectSequence.length)]; if (randomEffect && randomEffect.effect) { LK.getSound(randomEffect.effect).play(); } } self.currentBeat++; }; self.update = function () { var currentTime = Date.now(); if (currentTime - self.lastBeatTime >= self.beatInterval) { self.playBeat(); self.lastBeatTime = currentTime; // Create beat visualization var beatPulse = game.addChild(LK.getAsset('beatPulse', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 })); beatPulse.x = 1024; beatPulse.y = 1366; tween(beatPulse, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 300, onFinish: function onFinish() { beatPulse.destroy(); } }); } }; return self; }); var Particle = Container.expand(function () { var self = Container.call(this); self.graphic = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = (Math.random() - 0.5) * 10; self.velocityY = (Math.random() - 0.5) * 10; self.life = 60; // 1 second at 60fps self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.life--; // Fade out over time self.graphic.alpha = self.life / 60; if (self.life <= 0) { self.destroy(); // Remove from particles array for (var i = particles.length - 1; i >= 0; i--) { if (particles[i] === self) { particles.splice(i, 1); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Face-to-Music System Variables // Face analysis shapes mapped to musical roles // Arcs for melody // Ellipses for bass // Squares for rhythm // Dots for effects // Scanning wireframe // Face wireframe // Beat visualization // Melody notes (piano) - mapped to arcs // Bass notes (synth bass) - mapped to ellipses // Rhythm sounds - mapped to squares // Sound effects - mapped to dots/lines // Expression triggers var facialAnalyzer; var musicSequencer; var expressionDetector; var scanEffectTimer = 0; var gameInitialized = false; // Musical arrays mapped to shape types var melodyNotes = ['melody-g3', 'melody-b3', 'melody-c4', 'melody-d4', 'melody-e4', 'melody-f4', 'melody-a4']; var bassNotes = ['bass-e2', 'bass-a2', 'bass-d2']; var rhythmSounds = ['kick', 'snare', 'hihat']; var effectSounds = ['filter-sweep', 'delay-effect', 'reverb-wash']; // UI Elements var scoreText; var instructionText; var bpmText; var statusText; var gameTime = 0; // Create score display scoreText = new Text2('Score: 0', { size: 60, fill: 0x00ff88 }); scoreText.anchor.set(0.5, 0); scoreText.y = 50; LK.gui.top.addChild(scoreText); // Create BPM display bpmText = new Text2('BPM: 120', { size: 40, fill: 0x88aaff }); bpmText.anchor.set(0, 0); bpmText.x = 50; bpmText.y = 120; LK.gui.top.addChild(bpmText); // Create status text statusText = new Text2('Scanning face...', { size: 50, fill: 0xffaa44 }); statusText.anchor.set(0.5, 0.5); LK.gui.center.addChild(statusText); // Create instruction text instructionText = new Text2('Express yourself to create music!', { size: 35, fill: 0xffffff }); instructionText.anchor.set(0.5, 1); instructionText.y = -50; LK.gui.bottom.addChild(instructionText); // Initialize face-to-music system components facialAnalyzer = game.addChild(new FacialAnalyzer()); musicSequencer = new MusicSequencer(); expressionDetector = new ExpressionDetector(); function createScanningEffect() { // Create multiple scanning lines for (var i = 0; i < 3; i++) { var scanLine = game.addChild(LK.getAsset('scanLine', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 })); scanLine.x = 1024; scanLine.y = 200 + i * 150; scanLine.scaleX = 0; tween(scanLine, { scaleX: 1.5, alpha: 0 }, { duration: 1000 + i * 200, onFinish: function onFinish() { scanLine.destroy(); } }); } } function createBeatVisualization(x, y, intensity) { var beatPulse = game.addChild(LK.getAsset('beatPulse', { anchorX: 0.5, anchorY: 0.5, alpha: intensity, tint: 0x00ff88 })); beatPulse.x = x; beatPulse.y = y; tween(beatPulse, { scaleX: 2 + intensity, scaleY: 2 + intensity, alpha: 0 }, { duration: 400, onFinish: function onFinish() { beatPulse.destroy(); } }); } function updateMusicVisualization() { // Create visual pulses synced to music if (musicSequencer.isPlaying && gameTime % 15 === 0) { var centerX = 1024; var centerY = 1366; createBeatVisualization(centerX, centerY, 0.8); // Create smaller pulses around face shapes if (facialAnalyzer.melodyArcs.length > 0) { for (var i = 0; i < facialAnalyzer.melodyArcs.length; i++) { var arc = facialAnalyzer.melodyArcs[i]; if (Math.random() < 0.3) { createBeatVisualization(arc.x, arc.y, 0.4); } } } } } // Handle tap to regenerate music composition game.down = function (x, y, obj) { if (gameInitialized && facialAnalyzer) { // Regenerate the musical composition facialAnalyzer.analyzeFace(); musicSequencer.generateSequence(facialAnalyzer); // Visual feedback facialAnalyzer.createScanEffect(); // Audio feedback LK.getSound('smile-trigger').play(); // Add score bonus LK.setScore(LK.getScore() + 15); scoreText.setText('Score: ' + LK.getScore()); // Temporary instruction var originalText = instructionText.text; instructionText.setText('Music composition refreshed!'); LK.setTimeout(function () { instructionText.setText(originalText); }, 2000); } }; // Main face-to-music game loop game.update = function () { gameTime++; // Check if face is detected and initialize system if (facekit.mouthCenter && facekit.leftEye && facekit.rightEye && !gameInitialized) { // Face detected - initialize the music system statusText.setText('Face detected! Analyzing...'); createScanningEffect(); // Allow some time for scanning effect if (scanEffectTimer++ > 120) { // 2 seconds at 60fps facialAnalyzer.analyzeFace(); musicSequencer.generateSequence(facialAnalyzer); gameInitialized = true; statusText.setText('Music composition created!'); // Fade out status text tween(statusText, { alpha: 0 }, { duration: 2000, onFinish: function onFinish() { statusText.setText(''); statusText.alpha = 1; } }); } } // Main music and visual system updates if (gameInitialized && facekit.mouthCenter) { // Update facial analysis continuously facialAnalyzer.analyzeFace(); // Update music sequencer with head tilt for panning if (facekit.leftEye && facekit.rightEye) { var headTilt = (facekit.rightEye.y - facekit.leftEye.y) / Math.abs(facekit.rightEye.x - facekit.leftEye.x); musicSequencer.updatePanning(headTilt); } // Update music sequencer musicSequencer.update(); // Update expression detection expressionDetector.update(musicSequencer); // Update music visualization updateMusicVisualization(); // Update UI bpmText.setText('BPM: ' + musicSequencer.bpm); // Re-analyze face every 10 seconds to maintain stability if (gameTime % 600 === 0) { // Doubled the interval for more stable music facialAnalyzer.analyzeFace(); musicSequencer.generateSequence(facialAnalyzer); } } // Update score based on active music creation if (gameInitialized && musicSequencer.isPlaying) { if (gameTime % 60 === 0) { LK.setScore(LK.getScore() + 2); scoreText.setText('Score: ' + LK.getScore()); } } // Update instruction text based on game state and score if (!gameInitialized) { if (facekit.mouthCenter) { instructionText.setText('Analyzing your face...'); } else { instructionText.setText('Position your face in view of camera'); } } else { if (LK.getScore() > 150) { instructionText.setText('Musical master! Your face creates beautiful compositions!'); } else if (LK.getScore() > 75) { instructionText.setText('Excellent! Try different expressions for variety!'); } else { instructionText.setText('Smile, frown, blink - each expression adds to your music!'); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var ExpressionDetector = Container.expand(function () {
var self = Container.call(this);
// Expression state tracking
self.lastSmiling = false;
self.lastEyebrowsFrowning = false;
self.lastBlinking = false;
self.blinkCounter = 0;
self.lastBlinkTime = 0;
// Head movement tracking
self.lastHeadX = 0;
self.lastHeadY = 0;
self.lastEyesOpen = true;
self.lastMouthPosition = 0;
self.lastNosePosition = 0;
// Expression thresholds
self.smileThreshold = 0.3;
self.frownThreshold = 0.2;
self.blinkThreshold = 50; // milliseconds
self.headMovementThreshold = 30;
self.eyeCloseThreshold = 20;
self.mouthMovementThreshold = 15;
self.noseMovementThreshold = 10;
self.detectSmile = function () {
if (!facekit.upperLip || !facekit.lowerLip) return false;
// Simple smile detection based on mouth corner positions
var mouthCurve = (facekit.upperLip.y - facekit.lowerLip.y) / 20;
return mouthCurve > self.smileThreshold;
};
self.detectFrown = function () {
if (!facekit.leftEye || !facekit.rightEye) return false;
// Detect frown based on eyebrow position relative to eyes
var eyebrowPosition = (facekit.leftEye.y + facekit.rightEye.y) / 2 - 30;
return eyebrowPosition < self.frownThreshold;
};
self.detectBlink = function () {
// Simplified blink detection (in real implementation would track eye closure)
var currentTime = Date.now();
if (currentTime - self.lastBlinkTime > 2000) {
// Auto-trigger every 2 seconds for demo
self.lastBlinkTime = currentTime;
return true;
}
return false;
};
self.detectHeadMovement = function () {
if (!facekit.mouthCenter) return {
left: false,
right: false,
up: false,
down: false
};
var currentHeadX = facekit.mouthCenter.x;
var currentHeadY = facekit.mouthCenter.y;
var deltaX = currentHeadX - self.lastHeadX;
var deltaY = currentHeadY - self.lastHeadY;
return {
left: deltaX < -self.headMovementThreshold,
right: deltaX > self.headMovementThreshold,
up: deltaY < -self.headMovementThreshold,
down: deltaY > self.headMovementThreshold
};
};
self.detectEyeMovement = function () {
if (!facekit.leftEye || !facekit.rightEye) return false;
// Detect eye closing based on eye position change
var eyeHeight = Math.abs(facekit.leftEye.y - facekit.rightEye.y);
var eyesCurrentlyOpen = eyeHeight > self.eyeCloseThreshold;
return eyesCurrentlyOpen !== self.lastEyesOpen;
};
self.detectMouthMovement = function () {
if (!facekit.mouthCenter) return false;
var currentMouthY = facekit.mouthCenter.y;
var deltaY = Math.abs(currentMouthY - self.lastMouthPosition);
return deltaY > self.mouthMovementThreshold;
};
self.detectNoseMovement = function () {
if (!facekit.noseTip) return false;
var currentNoseY = facekit.noseTip.y;
var deltaY = Math.abs(currentNoseY - self.lastNosePosition);
return deltaY > self.noseMovementThreshold;
};
self.onSmile = function (sequencer) {
// Add harmonious melody notes when smiling (reduced randomness)
var lastNoteIndex = sequencer.melodySequence.length > 0 ? sequencer.melodySequence[sequencer.melodySequence.length - 1].noteIndex || 0 : 0;
var harmonicNoteIndex = (lastNoteIndex + 2) % melodyNotes.length; // Always use harmonic intervals
var newMelodyNote = melodyNotes[harmonicNoteIndex];
LK.getSound(newMelodyNote).play();
LK.getSound('smile-trigger').play();
// Visual feedback
LK.effects.flashScreen(0x00ff88, 300);
// Add to score
LK.setScore(LK.getScore() + 25);
};
self.onFrown = function (sequencer) {
// Increase BPM when frowning
sequencer.bpm = Math.min(180, sequencer.bpm + 5);
sequencer.beatInterval = 60000 / sequencer.bpm;
LK.getSound('frown-trigger').play();
// Visual feedback - red flash
LK.effects.flashScreen(0xff4444, 200);
};
self.onBlink = function (sequencer) {
// Trigger random effects on blink
var randomEffect = effectSounds[Math.floor(Math.random() * effectSounds.length)];
LK.getSound(randomEffect).play();
LK.getSound('blink-effect').play();
// Create sparkle effect
for (var i = 0; i < 5; i++) {
var sparkle = game.addChild(LK.getAsset('effectDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1.0,
tint: Math.random() * 0xffffff
}));
sparkle.x = facekit.mouthCenter.x + (Math.random() - 0.5) * 200;
sparkle.y = facekit.mouthCenter.y + (Math.random() - 0.5) * 200;
tween(sparkle, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
};
self.onHeadMovement = function (direction, sequencer) {
// Different effects for different head movements
if (direction.left) {
// Pan music left and play bass note
sequencer.panValue = -0.8;
LK.getSound('bass-e2').play();
LK.effects.flashScreen(0x3498db, 300);
} else if (direction.right) {
// Pan music right and play different bass note
sequencer.panValue = 0.8;
LK.getSound('bass-a2').play();
LK.effects.flashScreen(0xe74c3c, 300);
} else if (direction.up) {
// Increase BPM and play high melody
sequencer.bpm = Math.min(160, sequencer.bpm + 10);
sequencer.beatInterval = 60000 / sequencer.bpm;
LK.getSound('melody-a4').play();
LK.effects.flashScreen(0xf1c40f, 200);
} else if (direction.down) {
// Decrease BPM and play low melody
sequencer.bpm = Math.max(80, sequencer.bpm - 10);
sequencer.beatInterval = 60000 / sequencer.bpm;
LK.getSound('melody-g3').play();
LK.effects.flashScreen(0x9b59b6, 200);
}
LK.setScore(LK.getScore() + 15);
};
self.onEyeMovement = function (sequencer) {
// Create eye-sparkle effect and play chord
var chordNotes = ['melody-c4', 'melody-e4', 'melody-g3'];
for (var i = 0; i < chordNotes.length; i++) {
LK.setTimeout(function (noteIndex) {
return function () {
LK.getSound(chordNotes[noteIndex]).play();
};
}(i), i * 100);
}
// Create eye glow effect
for (var i = 0; i < 8; i++) {
var glow = game.addChild(LK.getAsset('effectDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0x00ff88
}));
var eyeX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
var eyeY = (facekit.leftEye.y + facekit.rightEye.y) / 2;
var angle = i / 8 * Math.PI * 2;
glow.x = eyeX + Math.cos(angle) * 80;
glow.y = eyeY + Math.sin(angle) * 50;
tween(glow, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
glow.destroy();
}
});
}
LK.setScore(LK.getScore() + 20);
};
self.onMouthMovement = function (sequencer) {
// Play vocal-like sounds and create mouth wave effect
var vocalNotes = ['do-c', 're-d', 'fa-f', 'sol-g', 'la-a', 'si-b'];
var randomVocal = vocalNotes[Math.floor(Math.random() * vocalNotes.length)];
LK.getSound(randomVocal).play();
// Create wave effect from mouth
for (var i = 0; i < 6; i++) {
var wave = game.addChild(LK.getAsset('melodyArc', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
tint: 0xff6b6b
}));
wave.x = facekit.mouthCenter.x;
wave.y = facekit.mouthCenter.y;
wave.scaleX = 0.5;
wave.scaleY = 0.5;
tween(wave, {
scaleX: 2 + i * 0.3,
scaleY: 2 + i * 0.3,
alpha: 0
}, {
duration: 800 + i * 100,
onFinish: function onFinish() {
wave.destroy();
}
});
}
LK.setScore(LK.getScore() + 12);
};
self.onNoseMovement = function (sequencer) {
// Play filter effect and create nose particle burst
LK.getSound('filter-sweep').play();
// Create particle burst from nose
for (var i = 0; i < 10; i++) {
var particle = game.addChild(LK.getAsset('effectDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1.0,
tint: 0xf39c12
}));
particle.x = facekit.noseTip.x;
particle.y = facekit.noseTip.y;
var angle = Math.random() * Math.PI * 2;
var speed = 3 + Math.random() * 4;
var targetX = particle.x + Math.cos(angle) * speed * 30;
var targetY = particle.y + Math.sin(angle) * speed * 30;
tween(particle, {
x: targetX,
y: targetY,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
particle.destroy();
}
});
}
LK.setScore(LK.getScore() + 8);
};
self.update = function (sequencer) {
// Check for expression changes
var currentSmiling = self.detectSmile();
var currentFrowning = self.detectFrown();
var currentBlinking = self.detectBlink();
var headMovement = self.detectHeadMovement();
var eyeMovement = self.detectEyeMovement();
var mouthMovement = self.detectMouthMovement();
var noseMovement = self.detectNoseMovement();
// Trigger events on state transitions
if (!self.lastSmiling && currentSmiling) {
self.onSmile(sequencer);
}
if (!self.lastEyebrowsFrowning && currentFrowning) {
self.onFrown(sequencer);
}
if (currentBlinking) {
self.onBlink(sequencer);
}
// New movement detection
if (headMovement.left || headMovement.right || headMovement.up || headMovement.down) {
self.onHeadMovement(headMovement, sequencer);
}
if (eyeMovement) {
self.onEyeMovement(sequencer);
}
if (mouthMovement) {
self.onMouthMovement(sequencer);
}
if (noseMovement) {
self.onNoseMovement(sequencer);
}
// Update last states
self.lastSmiling = currentSmiling;
self.lastEyebrowsFrowning = currentFrowning;
// Update position tracking
if (facekit.mouthCenter) {
self.lastHeadX = facekit.mouthCenter.x;
self.lastHeadY = facekit.mouthCenter.y;
}
if (facekit.leftEye && facekit.rightEye) {
var eyeHeight = Math.abs(facekit.leftEye.y - facekit.rightEye.y);
self.lastEyesOpen = eyeHeight > self.eyeCloseThreshold;
}
if (facekit.mouthCenter) {
self.lastMouthPosition = facekit.mouthCenter.y;
}
if (facekit.noseTip) {
self.lastNosePosition = facekit.noseTip.y;
}
};
return self;
});
var FaceShape = Container.expand(function () {
var self = Container.call(this);
self.faceOutline = self.attachAsset('faceOutline', {
anchorX: 0.5,
anchorY: 0.5
});
self.leftEye = self.attachAsset('leftEye', {
anchorX: 0.5,
anchorY: 0.5
});
self.rightEye = self.attachAsset('rightEye', {
anchorX: 0.5,
anchorY: 0.5
});
self.nose = self.attachAsset('nose', {
anchorX: 0.5,
anchorY: 0.5
});
self.mouth = self.attachAsset('mouth', {
anchorX: 0.5,
anchorY: 0.5
});
self.currentColorIndex = 0;
self.colorPalettes = [[0x3498db, 0xe74c3c, 0xf39c12, 0x9b59b6], [0x2ecc71, 0xe67e22, 0x34495e, 0x1abc9c], [0xf1c40f, 0x8e44ad, 0x16a085, 0xc0392b], [0x95a5a6, 0x2980b9, 0x27ae60, 0xd35400]];
self.lastMouthOpen = false;
self.updateFacePosition = function (faceX, faceY, faceWidth, faceHeight) {
self.x = faceX;
self.y = faceY;
// Scale based on face size
var scale = Math.max(faceWidth / 300, faceHeight / 400) * 0.8;
self.faceOutline.scaleX = scale;
self.faceOutline.scaleY = scale;
// Position facial features relative to face center
self.leftEye.x = -faceWidth * 0.15;
self.leftEye.y = -faceHeight * 0.1;
self.rightEye.x = faceWidth * 0.15;
self.rightEye.y = -faceHeight * 0.1;
self.nose.x = 0;
self.nose.y = 0;
self.mouth.x = 0;
self.mouth.y = faceHeight * 0.15;
};
self.changeColors = function () {
self.currentColorIndex = (self.currentColorIndex + 1) % self.colorPalettes.length;
var palette = self.colorPalettes[self.currentColorIndex];
tween(self.faceOutline, {
tint: palette[0]
}, {
duration: 500
});
tween(self.leftEye, {
tint: palette[1]
}, {
duration: 500
});
tween(self.rightEye, {
tint: palette[1]
}, {
duration: 500
});
tween(self.nose, {
tint: palette[2]
}, {
duration: 500
});
tween(self.mouth, {
tint: palette[3]
}, {
duration: 500
});
LK.getSound('shapeChange').play();
};
self.update = function () {
// Check for mouth open expression change
var currentMouthOpen = facekit.mouthOpen;
if (!self.lastMouthOpen && currentMouthOpen) {
// Mouth just opened
self.triggerExpression();
}
self.lastMouthOpen = currentMouthOpen;
// Update mouth shape based on expression
if (currentMouthOpen) {
self.mouth.scaleY = 1.5;
} else {
self.mouth.scaleY = 1.0;
}
};
self.triggerExpression = function () {
// Create particle effect
createParticleEffect(self.x, self.y);
// Pulse effect on face outline
tween(self.faceOutline, {
scaleX: self.faceOutline.scaleX * 1.2,
scaleY: self.faceOutline.scaleY * 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.faceOutline, {
scaleX: self.faceOutline.scaleX / 1.2,
scaleY: self.faceOutline.scaleY / 1.2
}, {
duration: 200
});
}
});
// Play random note based on current face position and expression
var randomNoteIndex = Math.floor(Math.random() * noteIds.length);
LK.getSound(noteIds[randomNoteIndex]).play();
LK.getSound('expressionTrigger').play();
LK.setScore(LK.getScore() + 10);
scoreText.setText('Score: ' + LK.getScore());
};
return self;
});
var FacialAnalyzer = Container.expand(function () {
var self = Container.call(this);
// Musical shape arrays
self.melodyArcs = [];
self.bassEllipses = [];
self.rhythmSquares = [];
self.effectDots = [];
self.scanLines = [];
self.wireframes = [];
// Face analysis data
self.eyebrowAngle = 0;
self.eyeDistance = 0;
self.mouthWidth = 0;
self.chinLength = 0;
self.headTilt = 0;
// Musical timing
self.bpm = 120;
self.beatCount = 0;
self.lastBeatTime = 0;
self.analyzeFace = function () {
if (!facekit.leftEye || !facekit.rightEye || !facekit.mouthCenter) return;
// Calculate facial measurements
self.eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x);
self.mouthWidth = Math.abs(facekit.upperLip.x - facekit.lowerLip.x) || 50;
self.chinLength = Math.abs(facekit.chin.y - facekit.mouthCenter.y);
self.headTilt = (facekit.rightEye.y - facekit.leftEye.y) / self.eyeDistance;
// Generate shapes based on facial features
self.generateMelodyArcs();
self.generateBassEllipses();
self.generateRhythmSquares();
self.generateEffectDots();
};
self.generateMelodyArcs = function () {
// Clear existing arcs
for (var i = 0; i < self.melodyArcs.length; i++) {
self.melodyArcs[i].destroy();
}
self.melodyArcs = [];
// Create arcs based on eyebrow angle and eye distance
var arcCount = Math.floor(self.eyeDistance / 50) + 2;
for (var i = 0; i < arcCount; i++) {
var arc = self.addChild(LK.getAsset('melodyArc', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
arc.x = facekit.leftEye.x + i * (self.eyeDistance / arcCount);
arc.y = facekit.leftEye.y - 50;
arc.rotation = self.headTilt * 0.5;
arc.noteIndex = i % melodyNotes.length;
self.melodyArcs.push(arc);
}
};
self.generateBassEllipses = function () {
// Clear existing ellipses
for (var i = 0; i < self.bassEllipses.length; i++) {
self.bassEllipses[i].destroy();
}
self.bassEllipses = [];
// Create bass ellipses based on mouth width
var ellipseCount = Math.floor(self.mouthWidth / 30) + 1;
for (var i = 0; i < ellipseCount; i++) {
var ellipse = self.addChild(LK.getAsset('bassEllipse', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
ellipse.x = facekit.mouthCenter.x + (i - ellipseCount / 2) * 60;
ellipse.y = facekit.mouthCenter.y + 80;
ellipse.noteIndex = i % bassNotes.length;
self.bassEllipses.push(ellipse);
}
};
self.generateRhythmSquares = function () {
// Clear existing squares
for (var i = 0; i < self.rhythmSquares.length; i++) {
self.rhythmSquares[i].destroy();
}
self.rhythmSquares = [];
// Create rhythm squares based on chin length
var squareCount = Math.floor(self.chinLength / 40) + 2;
for (var i = 0; i < squareCount; i++) {
var square = self.addChild(LK.getAsset('rhythmSquare', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
}));
square.x = facekit.chin.x + (i - squareCount / 2) * 70;
square.y = facekit.chin.y + 50;
square.rhythmIndex = i % rhythmSounds.length;
self.rhythmSquares.push(square);
}
};
self.generateEffectDots = function () {
// Clear existing dots
for (var i = 0; i < self.effectDots.length; i++) {
self.effectDots[i].destroy();
}
self.effectDots = [];
// Create effect dots around nose area
var dotCount = 4;
for (var i = 0; i < dotCount; i++) {
var dot = self.addChild(LK.getAsset('effectDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
}));
var angle = i / dotCount * Math.PI * 2;
dot.x = facekit.noseTip.x + Math.cos(angle) * 60;
dot.y = facekit.noseTip.y + Math.sin(angle) * 40;
dot.effectIndex = i % effectSounds.length;
self.effectDots.push(dot);
}
};
self.createScanEffect = function () {
// Create scanning wireframe effect
var wireframe = self.addChild(LK.getAsset('faceWireframe', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
wireframe.x = facekit.mouthCenter.x;
wireframe.y = facekit.mouthCenter.y;
// Animate scanning effect
tween(wireframe, {
alpha: 0.8,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
tween(wireframe, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
onFinish: function onFinish() {
wireframe.destroy();
}
});
}
});
};
return self;
});
var MusicSequencer = Container.expand(function () {
var self = Container.call(this);
self.bpm = 120;
self.beatInterval = 60000 / self.bpm; // milliseconds per beat
self.lastBeatTime = 0;
self.currentBeat = 0;
self.isPlaying = false;
// Musical sequences based on face analysis
self.melodySequence = [];
self.bassSequence = [];
self.rhythmSequence = [];
self.effectSequence = [];
// Stereo panning based on head tilt
self.panValue = 0;
self.generateSequence = function (analyzer) {
// Generate melody sequence from arcs with reduced randomness
self.melodySequence = [];
// Create a more structured melody pattern
var baseNoteIndex = 0; // Start from root note
for (var i = 0; i < analyzer.melodyArcs.length; i++) {
var arc = analyzer.melodyArcs[i];
// Use sequential notes with small jumps for better harmony
var noteIndex = (baseNoteIndex + i) % melodyNotes.length;
// Limit note jumps to create smoother melodies
if (i > 0 && Math.abs(noteIndex - self.melodySequence[i - 1].noteIndex) > 2) {
noteIndex = (self.melodySequence[i - 1].noteIndex + 1) % melodyNotes.length;
}
self.melodySequence.push({
note: melodyNotes[noteIndex],
noteIndex: noteIndex,
beat: i % 4,
volume: 0.6 + arc.alpha * 0.4
});
}
// Generate bass sequence from ellipses
self.bassSequence = [];
for (var i = 0; i < analyzer.bassEllipses.length; i++) {
var ellipse = analyzer.bassEllipses[i];
self.bassSequence.push({
note: bassNotes[ellipse.noteIndex],
beat: i % 2,
volume: 0.8
});
}
// Generate rhythm sequence from squares
self.rhythmSequence = [];
for (var i = 0; i < analyzer.rhythmSquares.length; i++) {
var square = analyzer.rhythmSquares[i];
self.rhythmSequence.push({
sound: rhythmSounds[square.rhythmIndex],
beat: i % 4,
volume: 0.7
});
}
// Set up effect triggers from dots
self.effectSequence = [];
for (var i = 0; i < analyzer.effectDots.length; i++) {
var dot = analyzer.effectDots[i];
self.effectSequence.push({
effect: effectSounds[dot.effectIndex],
trigger: Math.random() < 0.3
});
}
self.isPlaying = true;
};
self.updatePanning = function (headTilt) {
self.panValue = Math.max(-1, Math.min(1, headTilt * 2));
// Apply panning to current sounds (simplified representation)
};
self.playBeat = function () {
if (!self.isPlaying) return;
var beatInSequence = self.currentBeat % 4;
// Play melody with reduced frequency for cleaner sound
var melodyCount = 0;
for (var i = 0; i < self.melodySequence.length; i++) {
var melodyNote = self.melodySequence[i];
if (melodyNote.beat === beatInSequence) {
// Only play every other melody note to reduce clutter
if (melodyCount % 2 === 0 || beatInSequence === 0) {
LK.getSound(melodyNote.note).play();
}
melodyCount++;
}
}
// Play bass
for (var i = 0; i < self.bassSequence.length; i++) {
var bassNote = self.bassSequence[i];
if (bassNote.beat === beatInSequence) {
LK.getSound(bassNote.note).play();
}
}
// Play rhythm
for (var i = 0; i < self.rhythmSequence.length; i++) {
var rhythmHit = self.rhythmSequence[i];
if (rhythmHit.beat === beatInSequence) {
LK.getSound(rhythmHit.sound).play();
}
}
// Reduced random effect triggers for cleaner composition
if (Math.random() < 0.03 && beatInSequence === 0) {
// Only on beat 0, much less frequent
var randomEffect = self.effectSequence[Math.floor(Math.random() * self.effectSequence.length)];
if (randomEffect && randomEffect.effect) {
LK.getSound(randomEffect.effect).play();
}
}
self.currentBeat++;
};
self.update = function () {
var currentTime = Date.now();
if (currentTime - self.lastBeatTime >= self.beatInterval) {
self.playBeat();
self.lastBeatTime = currentTime;
// Create beat visualization
var beatPulse = game.addChild(LK.getAsset('beatPulse', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
}));
beatPulse.x = 1024;
beatPulse.y = 1366;
tween(beatPulse, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
beatPulse.destroy();
}
});
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
self.graphic = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = (Math.random() - 0.5) * 10;
self.velocityY = (Math.random() - 0.5) * 10;
self.life = 60; // 1 second at 60fps
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.life--;
// Fade out over time
self.graphic.alpha = self.life / 60;
if (self.life <= 0) {
self.destroy();
// Remove from particles array
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i] === self) {
particles.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Face-to-Music System Variables
// Face analysis shapes mapped to musical roles
// Arcs for melody
// Ellipses for bass
// Squares for rhythm
// Dots for effects
// Scanning wireframe
// Face wireframe
// Beat visualization
// Melody notes (piano) - mapped to arcs
// Bass notes (synth bass) - mapped to ellipses
// Rhythm sounds - mapped to squares
// Sound effects - mapped to dots/lines
// Expression triggers
var facialAnalyzer;
var musicSequencer;
var expressionDetector;
var scanEffectTimer = 0;
var gameInitialized = false;
// Musical arrays mapped to shape types
var melodyNotes = ['melody-g3', 'melody-b3', 'melody-c4', 'melody-d4', 'melody-e4', 'melody-f4', 'melody-a4'];
var bassNotes = ['bass-e2', 'bass-a2', 'bass-d2'];
var rhythmSounds = ['kick', 'snare', 'hihat'];
var effectSounds = ['filter-sweep', 'delay-effect', 'reverb-wash'];
// UI Elements
var scoreText;
var instructionText;
var bpmText;
var statusText;
var gameTime = 0;
// Create score display
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0x00ff88
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 50;
LK.gui.top.addChild(scoreText);
// Create BPM display
bpmText = new Text2('BPM: 120', {
size: 40,
fill: 0x88aaff
});
bpmText.anchor.set(0, 0);
bpmText.x = 50;
bpmText.y = 120;
LK.gui.top.addChild(bpmText);
// Create status text
statusText = new Text2('Scanning face...', {
size: 50,
fill: 0xffaa44
});
statusText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(statusText);
// Create instruction text
instructionText = new Text2('Express yourself to create music!', {
size: 35,
fill: 0xffffff
});
instructionText.anchor.set(0.5, 1);
instructionText.y = -50;
LK.gui.bottom.addChild(instructionText);
// Initialize face-to-music system components
facialAnalyzer = game.addChild(new FacialAnalyzer());
musicSequencer = new MusicSequencer();
expressionDetector = new ExpressionDetector();
function createScanningEffect() {
// Create multiple scanning lines
for (var i = 0; i < 3; i++) {
var scanLine = game.addChild(LK.getAsset('scanLine', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
scanLine.x = 1024;
scanLine.y = 200 + i * 150;
scanLine.scaleX = 0;
tween(scanLine, {
scaleX: 1.5,
alpha: 0
}, {
duration: 1000 + i * 200,
onFinish: function onFinish() {
scanLine.destroy();
}
});
}
}
function createBeatVisualization(x, y, intensity) {
var beatPulse = game.addChild(LK.getAsset('beatPulse', {
anchorX: 0.5,
anchorY: 0.5,
alpha: intensity,
tint: 0x00ff88
}));
beatPulse.x = x;
beatPulse.y = y;
tween(beatPulse, {
scaleX: 2 + intensity,
scaleY: 2 + intensity,
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
beatPulse.destroy();
}
});
}
function updateMusicVisualization() {
// Create visual pulses synced to music
if (musicSequencer.isPlaying && gameTime % 15 === 0) {
var centerX = 1024;
var centerY = 1366;
createBeatVisualization(centerX, centerY, 0.8);
// Create smaller pulses around face shapes
if (facialAnalyzer.melodyArcs.length > 0) {
for (var i = 0; i < facialAnalyzer.melodyArcs.length; i++) {
var arc = facialAnalyzer.melodyArcs[i];
if (Math.random() < 0.3) {
createBeatVisualization(arc.x, arc.y, 0.4);
}
}
}
}
}
// Handle tap to regenerate music composition
game.down = function (x, y, obj) {
if (gameInitialized && facialAnalyzer) {
// Regenerate the musical composition
facialAnalyzer.analyzeFace();
musicSequencer.generateSequence(facialAnalyzer);
// Visual feedback
facialAnalyzer.createScanEffect();
// Audio feedback
LK.getSound('smile-trigger').play();
// Add score bonus
LK.setScore(LK.getScore() + 15);
scoreText.setText('Score: ' + LK.getScore());
// Temporary instruction
var originalText = instructionText.text;
instructionText.setText('Music composition refreshed!');
LK.setTimeout(function () {
instructionText.setText(originalText);
}, 2000);
}
};
// Main face-to-music game loop
game.update = function () {
gameTime++;
// Check if face is detected and initialize system
if (facekit.mouthCenter && facekit.leftEye && facekit.rightEye && !gameInitialized) {
// Face detected - initialize the music system
statusText.setText('Face detected! Analyzing...');
createScanningEffect();
// Allow some time for scanning effect
if (scanEffectTimer++ > 120) {
// 2 seconds at 60fps
facialAnalyzer.analyzeFace();
musicSequencer.generateSequence(facialAnalyzer);
gameInitialized = true;
statusText.setText('Music composition created!');
// Fade out status text
tween(statusText, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
statusText.setText('');
statusText.alpha = 1;
}
});
}
}
// Main music and visual system updates
if (gameInitialized && facekit.mouthCenter) {
// Update facial analysis continuously
facialAnalyzer.analyzeFace();
// Update music sequencer with head tilt for panning
if (facekit.leftEye && facekit.rightEye) {
var headTilt = (facekit.rightEye.y - facekit.leftEye.y) / Math.abs(facekit.rightEye.x - facekit.leftEye.x);
musicSequencer.updatePanning(headTilt);
}
// Update music sequencer
musicSequencer.update();
// Update expression detection
expressionDetector.update(musicSequencer);
// Update music visualization
updateMusicVisualization();
// Update UI
bpmText.setText('BPM: ' + musicSequencer.bpm);
// Re-analyze face every 10 seconds to maintain stability
if (gameTime % 600 === 0) {
// Doubled the interval for more stable music
facialAnalyzer.analyzeFace();
musicSequencer.generateSequence(facialAnalyzer);
}
}
// Update score based on active music creation
if (gameInitialized && musicSequencer.isPlaying) {
if (gameTime % 60 === 0) {
LK.setScore(LK.getScore() + 2);
scoreText.setText('Score: ' + LK.getScore());
}
}
// Update instruction text based on game state and score
if (!gameInitialized) {
if (facekit.mouthCenter) {
instructionText.setText('Analyzing your face...');
} else {
instructionText.setText('Position your face in view of camera');
}
} else {
if (LK.getScore() > 150) {
instructionText.setText('Musical master! Your face creates beautiful compositions!');
} else if (LK.getScore() > 75) {
instructionText.setText('Excellent! Try different expressions for variety!');
} else {
instructionText.setText('Smile, frown, blink - each expression adds to your music!');
}
}
};
do-c
Sound effect
re-d
Sound effect
fa-f
Sound effect
sol-g
Sound effect
la-a
Sound effect
si-b
Sound effect
melody-g3
Sound effect
melody-b3
Sound effect
melody-c4
Sound effect
melody-d4
Sound effect
melody-e4
Sound effect
melody-f4
Sound effect
melody-a4
Sound effect
smile-trigger
Sound effect
frown-trigger
Sound effect
blink-effect
Sound effect
shapeChange
Sound effect
kick
Sound effect
snare
Sound effect
hihat
Sound effect
filter-sweep
Sound effect
delay-effect
Sound effect
reverb-wash
Sound effect