/**** * 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 }); } };
/****
* 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
});
}
};