Code edit (1 edits merged)
Please save this source code
User prompt
Sing Along Studio
Initial prompt
Toca singing 2 (2017). Hold the mic 🎤 up nealty and now you try. Sing let it go and sing along with Elsa 🩵. Tap on the French button, or the Castilian Spanish button to make Elsa 🩵 sing in 2 different ways.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var LanguageButton = Container.expand(function (language) { var self = Container.call(this); var buttonBg = self.attachAsset('languageButton', { anchorX: 0.5, anchorY: 0.5 }); var langText = new Text2(language, { size: 30, fill: 0xFFFFFF }); langText.anchor.set(0.5, 0.5); self.addChild(langText); self.language = language; self.down = function (x, y, obj) { currentLanguage = self.language; updateLanguageButtons(); tween(buttonBg, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; self.up = function (x, y, obj) { tween(buttonBg, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var SongButton = Container.expand(function (songData) { var self = Container.call(this); var buttonBg = self.attachAsset('songButton', { anchorX: 0.5, anchorY: 0.5 }); var titleText = new Text2(songData.title, { size: 40, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); self.addChild(titleText); self.songData = songData; self.down = function (x, y, obj) { selectedSong = self.songData; gameState = 'playing'; initializeSong(); tween(buttonBg, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; self.up = function (x, y, obj) { tween(buttonBg, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var TargetNote = Container.expand(function (pitch, time) { var self = Container.call(this); var note = self.attachAsset('targetNote', { anchorX: 0.5, anchorY: 0.5 }); self.targetPitch = pitch; self.targetTime = time; return self; }); var VoiceParticle = Container.expand(function () { var self = Container.call(this); var particle = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.life = 1.0; self.velocityX = (Math.random() - 0.5) * 200; self.velocityY = (Math.random() - 0.5) * 200; self.update = function () { self.x += self.velocityX * 0.016; self.y += self.velocityY * 0.016; self.life -= 0.02; particle.alpha = self.life; particle.scaleX = self.life; particle.scaleY = self.life; if (self.life <= 0) { self.destroy(); var index = particles.indexOf(self); if (index > -1) { particles.splice(index, 1); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1A1A2E }); /**** * Game Code ****/ // Game state management var gameState = 'menu'; // 'menu', 'playing', 'results' var currentLanguage = 'English'; var selectedSong = null; var songProgress = 0; var songStartTime = 0; var score = 0; var maxScore = 100; // Voice tracking var lastVolume = 0; var lastPitch = 0; var pitchHistory = []; var volumeHistory = []; // Visual elements var particles = []; var targetNotes = []; var languageButtons = []; var songButtons = []; // Song data var songs = [{ id: 'twinkle', title: 'Twinkle Twinkle', duration: 12000, lyrics: { 'English': [{ text: 'Twinkle, twinkle, little star', time: 0, duration: 3000 }, { text: 'How I wonder what you are', time: 3000, duration: 3000 }, { text: 'Up above the world so high', time: 6000, duration: 3000 }, { text: 'Like a diamond in the sky', time: 9000, duration: 3000 }], 'Spanish': [{ text: 'Estrellita, dónde estás', time: 0, duration: 3000 }, { text: 'Me pregunto qué serás', time: 3000, duration: 3000 }, { text: 'En el cielo, tan arriba', time: 6000, duration: 3000 }, { text: 'Como un diamante que brilla', time: 9000, duration: 3000 }], 'French': [{ text: 'Brille, brille, petite étoile', time: 0, duration: 3000 }, { text: 'Je me demande ce que tu es', time: 3000, duration: 3000 }, { text: 'Là-haut dans le ciel si haut', time: 6000, duration: 3000 }, { text: 'Comme un diamant là-haut', time: 9000, duration: 3000 }] }, targetPitches: [{ pitch: 200, time: 0 }, { pitch: 200, time: 500 }, { pitch: 300, time: 1000 }, { pitch: 300, time: 1500 }, { pitch: 350, time: 2000 }, { pitch: 350, time: 2500 }, { pitch: 300, time: 3000 }, { pitch: 250, time: 3500 }, { pitch: 250, time: 4000 }, { pitch: 200, time: 4500 }, { pitch: 200, time: 5000 }, { pitch: 150, time: 5500 }] }, { id: 'happy', title: 'Happy Birthday', duration: 15000, lyrics: { 'English': [{ text: 'Happy birthday to you', time: 0, duration: 3000 }, { text: 'Happy birthday to you', time: 3000, duration: 3000 }, { text: 'Happy birthday dear friend', time: 6000, duration: 4500 }, { text: 'Happy birthday to you', time: 10500, duration: 4500 }], 'Spanish': [{ text: 'Cumpleaños feliz', time: 0, duration: 3000 }, { text: 'Cumpleaños feliz', time: 3000, duration: 3000 }, { text: 'Te deseamos amigo', time: 6000, duration: 4500 }, { text: 'Cumpleaños feliz', time: 10500, duration: 4500 }], 'French': [{ text: 'Joyeux anniversaire', time: 0, duration: 3000 }, { text: 'Joyeux anniversaire', time: 3000, duration: 3000 }, { text: 'Joyeux anniversaire mon ami', time: 6000, duration: 4500 }, { text: 'Joyeux anniversaire', time: 10500, duration: 4500 }] }, targetPitches: [{ pitch: 180, time: 0 }, { pitch: 180, time: 500 }, { pitch: 220, time: 1000 }, { pitch: 180, time: 1500 }, { pitch: 260, time: 2000 }, { pitch: 240, time: 2500 }] }]; // UI Elements var titleText = new Text2('Sing Along Studio', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 200; game.addChild(titleText); var micIcon = game.attachAsset('microphoneIcon', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 400 }); var volumeBar = game.attachAsset('noteBar', { anchorX: 0, anchorY: 0.5, x: 624, y: 500 }); var pitchIndicator = game.attachAsset('playerNote', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 600 }); var lyricBg = game.attachAsset('lyricBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2400 }); lyricBg.alpha = 0.8; var currentLyricText = new Text2('', { size: 50, fill: 0xFFFFFF }); currentLyricText.anchor.set(0.5, 0.5); currentLyricText.x = 1024; currentLyricText.y = 2400; game.addChild(currentLyricText); var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); // Language buttons var languages = ['English', 'Spanish', 'French']; for (var i = 0; i < languages.length; i++) { var langBtn = new LanguageButton(languages[i]); langBtn.x = 400 + i * 250; langBtn.y = 1000; game.addChild(langBtn); languageButtons.push(langBtn); } // Song selection buttons for (var i = 0; i < songs.length; i++) { var songBtn = new SongButton(songs[i]); songBtn.x = 1024; songBtn.y = 1300 + i * 150; game.addChild(songBtn); songButtons.push(songBtn); } // Back button var backButton = game.attachAsset('languageButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 150 }); var backText = new Text2('Back', { size: 30, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backText.x = 200; backText.y = 150; game.addChild(backText); function updateLanguageButtons() { for (var i = 0; i < languageButtons.length; i++) { var btn = languageButtons[i]; if (btn.language === currentLanguage) { tween(btn, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200 }); } else { tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } } } function initializeSong() { if (!selectedSong) return; songStartTime = LK.ticks; songProgress = 0; score = 0; // Clear existing target notes for (var i = 0; i < targetNotes.length; i++) { targetNotes[i].destroy(); } targetNotes = []; // Hide menu elements titleText.visible = false; for (var i = 0; i < languageButtons.length; i++) { languageButtons[i].visible = false; } for (var i = 0; i < songButtons.length; i++) { songButtons[i].visible = false; } // Show game elements lyricBg.visible = true; currentLyricText.visible = true; } function endSong() { gameState = 'results'; // Calculate final score var finalScore = Math.floor(score / maxScore * 100); LK.setScore(finalScore); // Show results currentLyricText.setText('Great job! Score: ' + finalScore + '%'); // Auto return to menu after 3 seconds LK.setTimeout(function () { returnToMenu(); }, 3000); } function returnToMenu() { gameState = 'menu'; selectedSong = null; // Show menu elements titleText.visible = true; for (var i = 0; i < languageButtons.length; i++) { languageButtons[i].visible = true; } for (var i = 0; i < songButtons.length; i++) { songButtons[i].visible = true; } // Hide game elements lyricBg.visible = false; currentLyricText.visible = false; currentLyricText.setText(''); } function createVoiceParticles() { if (facekit.volume > 0.3) { for (var i = 0; i < 3; i++) { var particle = new VoiceParticle(); particle.x = micIcon.x + (Math.random() - 0.5) * 100; particle.y = micIcon.y + (Math.random() - 0.5) * 100; game.addChild(particle); particles.push(particle); } } } function updatePitchVisualization() { var targetY = 600; if (facekit.pitch > 0) { // Map pitch to vertical position (lower pitch = lower on screen) targetY = 800 - facekit.pitch / 400 * 300; targetY = Math.max(400, Math.min(800, targetY)); } tween(pitchIndicator, { y: targetY }, { duration: 100 }); // Update pitch indicator size based on volume var scale = 0.5 + facekit.volume * 1.5; tween(pitchIndicator, { scaleX: scale, scaleY: scale }, { duration: 100 }); } function updateVolumeBar() { var barWidth = facekit.volume * 800; tween(volumeBar, { width: barWidth }, { duration: 100 }); } function calculatePitchScore() { if (!selectedSong || gameState !== 'playing') return; var currentTime = LK.ticks - songStartTime; var timeInMs = currentTime / 60 * 1000; // Find closest target pitch var closestPitch = null; var minTimeDiff = Infinity; for (var i = 0; i < selectedSong.targetPitches.length; i++) { var targetPitch = selectedSong.targetPitches[i]; var timeDiff = Math.abs(targetPitch.time - timeInMs); if (timeDiff < minTimeDiff && timeDiff < 1000) { minTimeDiff = timeDiff; closestPitch = targetPitch; } } if (closestPitch && facekit.pitch > 0) { var pitchDiff = Math.abs(facekit.pitch - closestPitch.pitch); var pitchScore = Math.max(0, 1 - pitchDiff / 100); var volumeBonus = facekit.volume > 0.3 ? 1 : 0.5; score += pitchScore * volumeBonus * 0.1; score = Math.min(score, maxScore); } } function updateCurrentLyric() { if (!selectedSong || gameState !== 'playing') return; var currentTime = LK.ticks - songStartTime; var timeInMs = currentTime / 60 * 1000; var lyrics = selectedSong.lyrics[currentLanguage]; var currentLyric = ''; for (var i = 0; i < lyrics.length; i++) { var lyric = lyrics[i]; if (timeInMs >= lyric.time && timeInMs < lyric.time + lyric.duration) { currentLyric = lyric.text; break; } } if (currentLyricText.text !== currentLyric) { currentLyricText.setText(currentLyric); if (currentLyric !== '') { tween(currentLyricText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(currentLyricText, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); } } } // Event handlers game.down = function (x, y, obj) { if (gameState === 'results' || gameState === 'playing') { // Check if back button clicked if (x < 300 && y < 250) { returnToMenu(); } } }; // Initialize menu state updateLanguageButtons(); lyricBg.visible = false; currentLyricText.visible = false; game.update = function () { // Update volume history volumeHistory.push(facekit.volume); if (volumeHistory.length > 30) { volumeHistory.shift(); } // Update pitch history pitchHistory.push(facekit.pitch); if (pitchHistory.length > 30) { pitchHistory.shift(); } // Update visual feedback updateVolumeBar(); updatePitchVisualization(); // Create particles based on voice input if (LK.ticks % 10 === 0) { createVoiceParticles(); } // Update score display scoreText.setText('Score: ' + Math.floor(score)); if (gameState === 'playing' && selectedSong) { var currentTime = LK.ticks - songStartTime; var timeInMs = currentTime / 60 * 1000; // Update lyrics updateCurrentLyric(); // Calculate pitch accuracy score calculatePitchScore(); // Check if song is finished if (timeInMs >= selectedSong.duration) { endSong(); } // Add visual effects for good singing if (facekit.volume > 0.6 && facekit.pitch > 100) { LK.effects.flashObject(micIcon, 0x4CAF50, 200); } } // Animate microphone icon based on volume if (facekit.volume > 0.1) { var pulse = 1 + facekit.volume * 0.3; micIcon.scaleX = pulse; micIcon.scaleY = pulse; } else { tween(micIcon, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } // Color microphone based on pitch detection if (facekit.pitch > 50) { tween(micIcon, { tint: 0x4CAF50 }, { duration: 200 }); } else { tween(micIcon, { tint: 0xFFFFFF }, { duration: 200 }); } };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,637 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var facekit = LK.import("@upit/facekit.v1");
+var storage = LK.import("@upit/storage.v1");
+
+/****
+* Classes
+****/
+var LanguageButton = Container.expand(function (language) {
+ var self = Container.call(this);
+ var buttonBg = self.attachAsset('languageButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var langText = new Text2(language, {
+ size: 30,
+ fill: 0xFFFFFF
+ });
+ langText.anchor.set(0.5, 0.5);
+ self.addChild(langText);
+ self.language = language;
+ self.down = function (x, y, obj) {
+ currentLanguage = self.language;
+ updateLanguageButtons();
+ tween(buttonBg, {
+ scaleX: 0.9,
+ scaleY: 0.9
+ }, {
+ duration: 100
+ });
+ };
+ self.up = function (x, y, obj) {
+ tween(buttonBg, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 100
+ });
+ };
+ return self;
+});
+var SongButton = Container.expand(function (songData) {
+ var self = Container.call(this);
+ var buttonBg = self.attachAsset('songButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var titleText = new Text2(songData.title, {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ titleText.anchor.set(0.5, 0.5);
+ self.addChild(titleText);
+ self.songData = songData;
+ self.down = function (x, y, obj) {
+ selectedSong = self.songData;
+ gameState = 'playing';
+ initializeSong();
+ tween(buttonBg, {
+ scaleX: 0.9,
+ scaleY: 0.9
+ }, {
+ duration: 100
+ });
+ };
+ self.up = function (x, y, obj) {
+ tween(buttonBg, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 100
+ });
+ };
+ return self;
+});
+var TargetNote = Container.expand(function (pitch, time) {
+ var self = Container.call(this);
+ var note = self.attachAsset('targetNote', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.targetPitch = pitch;
+ self.targetTime = time;
+ return self;
+});
+var VoiceParticle = Container.expand(function () {
+ var self = Container.call(this);
+ var particle = self.attachAsset('particle', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.life = 1.0;
+ self.velocityX = (Math.random() - 0.5) * 200;
+ self.velocityY = (Math.random() - 0.5) * 200;
+ self.update = function () {
+ self.x += self.velocityX * 0.016;
+ self.y += self.velocityY * 0.016;
+ self.life -= 0.02;
+ particle.alpha = self.life;
+ particle.scaleX = self.life;
+ particle.scaleY = self.life;
+ if (self.life <= 0) {
+ self.destroy();
+ var index = particles.indexOf(self);
+ if (index > -1) {
+ particles.splice(index, 1);
+ }
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x1A1A2E
+});
+
+/****
+* Game Code
+****/
+// Game state management
+var gameState = 'menu'; // 'menu', 'playing', 'results'
+var currentLanguage = 'English';
+var selectedSong = null;
+var songProgress = 0;
+var songStartTime = 0;
+var score = 0;
+var maxScore = 100;
+// Voice tracking
+var lastVolume = 0;
+var lastPitch = 0;
+var pitchHistory = [];
+var volumeHistory = [];
+// Visual elements
+var particles = [];
+var targetNotes = [];
+var languageButtons = [];
+var songButtons = [];
+// Song data
+var songs = [{
+ id: 'twinkle',
+ title: 'Twinkle Twinkle',
+ duration: 12000,
+ lyrics: {
+ 'English': [{
+ text: 'Twinkle, twinkle, little star',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'How I wonder what you are',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'Up above the world so high',
+ time: 6000,
+ duration: 3000
+ }, {
+ text: 'Like a diamond in the sky',
+ time: 9000,
+ duration: 3000
+ }],
+ 'Spanish': [{
+ text: 'Estrellita, dónde estás',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'Me pregunto qué serás',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'En el cielo, tan arriba',
+ time: 6000,
+ duration: 3000
+ }, {
+ text: 'Como un diamante que brilla',
+ time: 9000,
+ duration: 3000
+ }],
+ 'French': [{
+ text: 'Brille, brille, petite étoile',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'Je me demande ce que tu es',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'Là-haut dans le ciel si haut',
+ time: 6000,
+ duration: 3000
+ }, {
+ text: 'Comme un diamant là-haut',
+ time: 9000,
+ duration: 3000
+ }]
+ },
+ targetPitches: [{
+ pitch: 200,
+ time: 0
+ }, {
+ pitch: 200,
+ time: 500
+ }, {
+ pitch: 300,
+ time: 1000
+ }, {
+ pitch: 300,
+ time: 1500
+ }, {
+ pitch: 350,
+ time: 2000
+ }, {
+ pitch: 350,
+ time: 2500
+ }, {
+ pitch: 300,
+ time: 3000
+ }, {
+ pitch: 250,
+ time: 3500
+ }, {
+ pitch: 250,
+ time: 4000
+ }, {
+ pitch: 200,
+ time: 4500
+ }, {
+ pitch: 200,
+ time: 5000
+ }, {
+ pitch: 150,
+ time: 5500
+ }]
+}, {
+ id: 'happy',
+ title: 'Happy Birthday',
+ duration: 15000,
+ lyrics: {
+ 'English': [{
+ text: 'Happy birthday to you',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'Happy birthday to you',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'Happy birthday dear friend',
+ time: 6000,
+ duration: 4500
+ }, {
+ text: 'Happy birthday to you',
+ time: 10500,
+ duration: 4500
+ }],
+ 'Spanish': [{
+ text: 'Cumpleaños feliz',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'Cumpleaños feliz',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'Te deseamos amigo',
+ time: 6000,
+ duration: 4500
+ }, {
+ text: 'Cumpleaños feliz',
+ time: 10500,
+ duration: 4500
+ }],
+ 'French': [{
+ text: 'Joyeux anniversaire',
+ time: 0,
+ duration: 3000
+ }, {
+ text: 'Joyeux anniversaire',
+ time: 3000,
+ duration: 3000
+ }, {
+ text: 'Joyeux anniversaire mon ami',
+ time: 6000,
+ duration: 4500
+ }, {
+ text: 'Joyeux anniversaire',
+ time: 10500,
+ duration: 4500
+ }]
+ },
+ targetPitches: [{
+ pitch: 180,
+ time: 0
+ }, {
+ pitch: 180,
+ time: 500
+ }, {
+ pitch: 220,
+ time: 1000
+ }, {
+ pitch: 180,
+ time: 1500
+ }, {
+ pitch: 260,
+ time: 2000
+ }, {
+ pitch: 240,
+ time: 2500
+ }]
+}];
+// UI Elements
+var titleText = new Text2('Sing Along Studio', {
+ size: 80,
+ fill: 0xFFFFFF
+});
+titleText.anchor.set(0.5, 0.5);
+titleText.x = 1024;
+titleText.y = 200;
+game.addChild(titleText);
+var micIcon = game.attachAsset('microphoneIcon', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: 400
+});
+var volumeBar = game.attachAsset('noteBar', {
+ anchorX: 0,
+ anchorY: 0.5,
+ x: 624,
+ y: 500
+});
+var pitchIndicator = game.attachAsset('playerNote', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: 600
+});
+var lyricBg = game.attachAsset('lyricBackground', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: 2400
+});
+lyricBg.alpha = 0.8;
+var currentLyricText = new Text2('', {
+ size: 50,
+ fill: 0xFFFFFF
+});
+currentLyricText.anchor.set(0.5, 0.5);
+currentLyricText.x = 1024;
+currentLyricText.y = 2400;
+game.addChild(currentLyricText);
+var scoreText = new Text2('Score: 0', {
+ size: 40,
+ fill: 0xFFFFFF
+});
+scoreText.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreText);
+// Language buttons
+var languages = ['English', 'Spanish', 'French'];
+for (var i = 0; i < languages.length; i++) {
+ var langBtn = new LanguageButton(languages[i]);
+ langBtn.x = 400 + i * 250;
+ langBtn.y = 1000;
+ game.addChild(langBtn);
+ languageButtons.push(langBtn);
+}
+// Song selection buttons
+for (var i = 0; i < songs.length; i++) {
+ var songBtn = new SongButton(songs[i]);
+ songBtn.x = 1024;
+ songBtn.y = 1300 + i * 150;
+ game.addChild(songBtn);
+ songButtons.push(songBtn);
+}
+// Back button
+var backButton = game.attachAsset('languageButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 200,
+ y: 150
+});
+var backText = new Text2('Back', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+backText.anchor.set(0.5, 0.5);
+backText.x = 200;
+backText.y = 150;
+game.addChild(backText);
+function updateLanguageButtons() {
+ for (var i = 0; i < languageButtons.length; i++) {
+ var btn = languageButtons[i];
+ if (btn.language === currentLanguage) {
+ tween(btn, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 200
+ });
+ } else {
+ tween(btn, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ }
+}
+function initializeSong() {
+ if (!selectedSong) return;
+ songStartTime = LK.ticks;
+ songProgress = 0;
+ score = 0;
+ // Clear existing target notes
+ for (var i = 0; i < targetNotes.length; i++) {
+ targetNotes[i].destroy();
+ }
+ targetNotes = [];
+ // Hide menu elements
+ titleText.visible = false;
+ for (var i = 0; i < languageButtons.length; i++) {
+ languageButtons[i].visible = false;
+ }
+ for (var i = 0; i < songButtons.length; i++) {
+ songButtons[i].visible = false;
+ }
+ // Show game elements
+ lyricBg.visible = true;
+ currentLyricText.visible = true;
+}
+function endSong() {
+ gameState = 'results';
+ // Calculate final score
+ var finalScore = Math.floor(score / maxScore * 100);
+ LK.setScore(finalScore);
+ // Show results
+ currentLyricText.setText('Great job! Score: ' + finalScore + '%');
+ // Auto return to menu after 3 seconds
+ LK.setTimeout(function () {
+ returnToMenu();
+ }, 3000);
+}
+function returnToMenu() {
+ gameState = 'menu';
+ selectedSong = null;
+ // Show menu elements
+ titleText.visible = true;
+ for (var i = 0; i < languageButtons.length; i++) {
+ languageButtons[i].visible = true;
+ }
+ for (var i = 0; i < songButtons.length; i++) {
+ songButtons[i].visible = true;
+ }
+ // Hide game elements
+ lyricBg.visible = false;
+ currentLyricText.visible = false;
+ currentLyricText.setText('');
+}
+function createVoiceParticles() {
+ if (facekit.volume > 0.3) {
+ for (var i = 0; i < 3; i++) {
+ var particle = new VoiceParticle();
+ particle.x = micIcon.x + (Math.random() - 0.5) * 100;
+ particle.y = micIcon.y + (Math.random() - 0.5) * 100;
+ game.addChild(particle);
+ particles.push(particle);
+ }
+ }
+}
+function updatePitchVisualization() {
+ var targetY = 600;
+ if (facekit.pitch > 0) {
+ // Map pitch to vertical position (lower pitch = lower on screen)
+ targetY = 800 - facekit.pitch / 400 * 300;
+ targetY = Math.max(400, Math.min(800, targetY));
+ }
+ tween(pitchIndicator, {
+ y: targetY
+ }, {
+ duration: 100
+ });
+ // Update pitch indicator size based on volume
+ var scale = 0.5 + facekit.volume * 1.5;
+ tween(pitchIndicator, {
+ scaleX: scale,
+ scaleY: scale
+ }, {
+ duration: 100
+ });
+}
+function updateVolumeBar() {
+ var barWidth = facekit.volume * 800;
+ tween(volumeBar, {
+ width: barWidth
+ }, {
+ duration: 100
+ });
+}
+function calculatePitchScore() {
+ if (!selectedSong || gameState !== 'playing') return;
+ var currentTime = LK.ticks - songStartTime;
+ var timeInMs = currentTime / 60 * 1000;
+ // Find closest target pitch
+ var closestPitch = null;
+ var minTimeDiff = Infinity;
+ for (var i = 0; i < selectedSong.targetPitches.length; i++) {
+ var targetPitch = selectedSong.targetPitches[i];
+ var timeDiff = Math.abs(targetPitch.time - timeInMs);
+ if (timeDiff < minTimeDiff && timeDiff < 1000) {
+ minTimeDiff = timeDiff;
+ closestPitch = targetPitch;
+ }
+ }
+ if (closestPitch && facekit.pitch > 0) {
+ var pitchDiff = Math.abs(facekit.pitch - closestPitch.pitch);
+ var pitchScore = Math.max(0, 1 - pitchDiff / 100);
+ var volumeBonus = facekit.volume > 0.3 ? 1 : 0.5;
+ score += pitchScore * volumeBonus * 0.1;
+ score = Math.min(score, maxScore);
+ }
+}
+function updateCurrentLyric() {
+ if (!selectedSong || gameState !== 'playing') return;
+ var currentTime = LK.ticks - songStartTime;
+ var timeInMs = currentTime / 60 * 1000;
+ var lyrics = selectedSong.lyrics[currentLanguage];
+ var currentLyric = '';
+ for (var i = 0; i < lyrics.length; i++) {
+ var lyric = lyrics[i];
+ if (timeInMs >= lyric.time && timeInMs < lyric.time + lyric.duration) {
+ currentLyric = lyric.text;
+ break;
+ }
+ }
+ if (currentLyricText.text !== currentLyric) {
+ currentLyricText.setText(currentLyric);
+ if (currentLyric !== '') {
+ tween(currentLyricText, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ tween(currentLyricText, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ });
+ }
+ }
+}
+// Event handlers
+game.down = function (x, y, obj) {
+ if (gameState === 'results' || gameState === 'playing') {
+ // Check if back button clicked
+ if (x < 300 && y < 250) {
+ returnToMenu();
+ }
+ }
+};
+// Initialize menu state
+updateLanguageButtons();
+lyricBg.visible = false;
+currentLyricText.visible = false;
+game.update = function () {
+ // Update volume history
+ volumeHistory.push(facekit.volume);
+ if (volumeHistory.length > 30) {
+ volumeHistory.shift();
+ }
+ // Update pitch history
+ pitchHistory.push(facekit.pitch);
+ if (pitchHistory.length > 30) {
+ pitchHistory.shift();
+ }
+ // Update visual feedback
+ updateVolumeBar();
+ updatePitchVisualization();
+ // Create particles based on voice input
+ if (LK.ticks % 10 === 0) {
+ createVoiceParticles();
+ }
+ // Update score display
+ scoreText.setText('Score: ' + Math.floor(score));
+ if (gameState === 'playing' && selectedSong) {
+ var currentTime = LK.ticks - songStartTime;
+ var timeInMs = currentTime / 60 * 1000;
+ // Update lyrics
+ updateCurrentLyric();
+ // Calculate pitch accuracy score
+ calculatePitchScore();
+ // Check if song is finished
+ if (timeInMs >= selectedSong.duration) {
+ endSong();
+ }
+ // Add visual effects for good singing
+ if (facekit.volume > 0.6 && facekit.pitch > 100) {
+ LK.effects.flashObject(micIcon, 0x4CAF50, 200);
+ }
+ }
+ // Animate microphone icon based on volume
+ if (facekit.volume > 0.1) {
+ var pulse = 1 + facekit.volume * 0.3;
+ micIcon.scaleX = pulse;
+ micIcon.scaleY = pulse;
+ } else {
+ tween(micIcon, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ // Color microphone based on pitch detection
+ if (facekit.pitch > 50) {
+ tween(micIcon, {
+ tint: 0x4CAF50
+ }, {
+ duration: 200
+ });
+ } else {
+ tween(micIcon, {
+ tint: 0xFFFFFF
+ }, {
+ duration: 200
+ });
+ }
+};
\ No newline at end of file