# japanese-learning > Japanese language learning app combining video lessons, interactive games, flashcards with spaced repetition, text-to-speech pronunciation, vocabulary (400+ words), verbs (35+), kanji (50+), and progress tracking. - Author: sanctum - Repository: kinggkoopa/sanctum-network - Version: 20260202215920 - Stars: 1 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/kinggkoopa/sanctum-network - Web: https://mule.run/skillshub/@@kinggkoopa/sanctum-network~japanese-learning:20260202215920 --- --- name: japanese-learning description: Japanese language learning app combining video lessons, interactive games, flashcards with spaced repetition, text-to-speech pronunciation, vocabulary (400+ words), verbs (35+), kanji (50+), and progress tracking. version: 2.0 --- # Japanese Learning App Skill ## Overview Comprehensive Japanese learning platform combining: - Video lessons (streaming app integration) - Interactive learning games (8+ game types) - Flashcard system with spaced repetition - Text-to-speech pronunciation with Japanese voice - Progress tracking with statistics - 400+ vocabulary words, 35+ verbs, 50+ kanji ## File Structure ``` japanese/ ├── index.html # Splash page with mode selection ├── player.html # Video learning mode ├── learn.html # Interactive games and study ├── data.json # Video playlists and app config ``` ## Design Aesthetic ```css :root { --bg: #0c0a09; --surface: #1a1614; --surface-2: #242020; --surface-3: #2e2a2a; --text: #f5f0eb; --text-2: #a8a098; --text-3: #685850; --red: #dc2626; /* Japanese red */ --red-soft: rgba(220, 38, 38, 0.15); --gold: #d4af37; --blue: #3b82f6; /* Audio/interactive */ --green: #22c55e; /* Success */ --font-display: 'Noto Serif JP', serif; --font-body: 'Inter', sans-serif; --font-japanese: 'Noto Sans JP', sans-serif; } ``` ## Data Structures ### Vocabulary Data (400+ words) ```javascript const VOCABULARY = { greetings: [ { japanese: 'おはようございます', reading: 'ohayou gozaimasu', english: 'Good morning (polite)', level: 'N5' }, { japanese: 'こんにちは', reading: 'konnichiwa', english: 'Hello/Good afternoon', level: 'N5' }, { japanese: 'こんばんは', reading: 'konbanwa', english: 'Good evening', level: 'N5' }, { japanese: 'おやすみなさい', reading: 'oyasuminasai', english: 'Good night', level: 'N5' }, { japanese: 'さようなら', reading: 'sayounara', english: 'Goodbye', level: 'N5' }, { japanese: 'ありがとうございます', reading: 'arigatou gozaimasu', english: 'Thank you (polite)', level: 'N5' }, { japanese: 'すみません', reading: 'sumimasen', english: 'Excuse me / Sorry', level: 'N5' }, { japanese: 'いただきます', reading: 'itadakimasu', english: 'Expression before eating', level: 'N5' }, { japanese: 'ごちそうさまでした', reading: 'gochisousama deshita', english: 'Expression after eating', level: 'N5' } ], food: [ { japanese: 'ごはん', reading: 'gohan', english: 'Rice / Meal', level: 'N5' }, { japanese: 'パン', reading: 'pan', english: 'Bread', level: 'N5' }, { japanese: '水', reading: 'mizu', english: 'Water', level: 'N5' }, { japanese: 'お茶', reading: 'ocha', english: 'Tea', level: 'N5' }, { japanese: 'コーヒー', reading: 'koohii', english: 'Coffee', level: 'N5' }, { japanese: '肉', reading: 'niku', english: 'Meat', level: 'N5' }, { japanese: '魚', reading: 'sakana', english: 'Fish', level: 'N5' }, { japanese: '野菜', reading: 'yasai', english: 'Vegetables', level: 'N5' } ], // ... more categories: family, numbers, time, places, adjectives, etc. }; ``` ### Verb Data (35+ verbs with conjugations) ```javascript const VERBS = { godan: [ { dictionary: '食べる', reading: 'taberu', english: 'to eat', type: 'ichidan', conjugations: { masu: '食べます', te: '食べて', ta: '食べた', nai: '食べない', potential: '食べられる', passive: '食べられる', causative: '食べさせる', imperative: '食べろ', volitional: '食べよう' } }, { dictionary: '行く', reading: 'iku', english: 'to go', type: 'godan', conjugations: { masu: '行きます', te: '行って', ta: '行った', nai: '行かない', potential: '行ける', passive: '行かれる', causative: '行かせる' } } // ... more verbs ] }; ``` ### Kanji Data (50+ kanji) ```javascript const KANJI = { n5: [ { kanji: '日', meanings: ['day', 'sun', 'Japan'], onyomi: ['ニチ', 'ジツ'], kunyomi: ['ひ', 'か'], examples: [ { word: '日本', reading: 'にほん', meaning: 'Japan' }, { word: '今日', reading: 'きょう', meaning: 'today' }, { word: '日曜日', reading: 'にちようび', meaning: 'Sunday' } ], strokes: 4, radical: '日' }, { kanji: '月', meanings: ['month', 'moon'], onyomi: ['ゲツ', 'ガツ'], kunyomi: ['つき'], examples: [ { word: '月曜日', reading: 'げつようび', meaning: 'Monday' }, { word: '一月', reading: 'いちがつ', meaning: 'January' } ], strokes: 4, radical: '月' } // ... more kanji ] }; ``` ### Hiragana & Katakana ```javascript const HIRAGANA = { basic: [ { char: 'あ', romaji: 'a', row: 'vowel' }, { char: 'い', romaji: 'i', row: 'vowel' }, { char: 'う', romaji: 'u', row: 'vowel' }, { char: 'え', romaji: 'e', row: 'vowel' }, { char: 'お', romaji: 'o', row: 'vowel' }, { char: 'か', romaji: 'ka', row: 'k' }, { char: 'き', romaji: 'ki', row: 'k' }, // ... complete 46 basic hiragana ], dakuten: [ { char: 'が', romaji: 'ga', row: 'g' }, { char: 'ぎ', romaji: 'gi', row: 'g' }, // ... voiced consonants ], combo: [ { char: 'きゃ', romaji: 'kya' }, { char: 'きゅ', romaji: 'kyu' }, { char: 'きょ', romaji: 'kyo' }, // ... combination characters ] }; const KATAKANA = { // Same structure as hiragana basic: [ { char: 'ア', romaji: 'a', row: 'vowel' }, // ... ] }; ``` ## Text-to-Speech Implementation ### CRITICAL: Browser Speech Synthesis ```javascript let japaneseVoice = null; let speechReady = false; function initVoices() { const voices = speechSynthesis.getVoices(); // Priority order for Japanese voices japaneseVoice = voices.find(v => v.lang === 'ja-JP' && v.name.includes('Google')) || voices.find(v => v.lang === 'ja-JP') || voices.find(v => v.lang.startsWith('ja')) || voices.find(v => v.name.toLowerCase().includes('japanese')); speechReady = true; console.log('Japanese voice:', japaneseVoice?.name || 'Using default'); } // Voices load asynchronously - MUST handle this if (speechSynthesis.getVoices().length > 0) { initVoices(); } speechSynthesis.onvoiceschanged = initVoices; function speak(text, rate = 0.8) { if (!speechReady) { console.warn('Speech not ready yet'); return; } // Cancel any current speech speechSynthesis.cancel(); const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'ja-JP'; utterance.rate = rate; utterance.volume = 1.0; if (japaneseVoice) { utterance.voice = japaneseVoice; } // Visual feedback utterance.onstart = () => { document.querySelectorAll('.audio-btn.active').forEach(b => b.classList.remove('active')); event?.target?.classList?.add('active'); }; utterance.onend = () => { event?.target?.classList?.remove('active'); }; utterance.onerror = (e) => console.error('Speech error:', e); speechSynthesis.speak(utterance); } ``` ### Audio Button with Tactile Feedback ```html ``` ```css .audio-btn { width: 48px; height: 48px; background: linear-gradient(145deg, var(--blue), #2563eb); border: none; border-radius: 50%; color: white; font-size: 1.2rem; cursor: pointer; transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); } .audio-btn:hover { transform: scale(1.1) translateY(-2px); box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4); } .audio-btn:active { transform: scale(0.95); background: var(--green); } .audio-btn.active { animation: pulse 0.5s ease-in-out infinite; background: var(--green); } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } ``` ## Game Types ### 1. Flashcard Mode ```javascript function FlashcardGame(category) { let cards = shuffle([...VOCABULARY[category]]); let currentIndex = 0; let showAnswer = false; function render() { const card = cards[currentIndex]; return `
${card.japanese}
${card.reading}
${card.english}
${currentIndex + 1} / ${cards.length}
`; } } ``` ### 2. Multiple Choice Quiz ```javascript function QuizGame(category, count = 10) { const questions = shuffle([...VOCABULARY[category]]).slice(0, count); let currentQuestion = 0; let score = 0; function generateOptions(correct) { const wrong = shuffle(VOCABULARY[category].filter(w => w !== correct)).slice(0, 3); return shuffle([correct, ...wrong]); } function render() { const q = questions[currentQuestion]; const options = generateOptions(q); return `
${q.japanese}
${options.map((opt, i) => ` `).join('')}
Score: ${score}/${currentQuestion}
`; } } ``` ### 3. Typing Practice ```javascript function TypingGame(kanaSet) { const chars = shuffle([...kanaSet]); let currentIndex = 0; let input = ''; function checkInput(typed) { const current = chars[currentIndex]; if (typed.toLowerCase() === current.romaji) { // Correct! playSound('correct'); currentIndex++; input = ''; if (currentIndex >= chars.length) { showResults(); } } } function render() { const current = chars[currentIndex]; return `
${current.char}
${currentIndex}/${chars.length}
`; } } ``` ### 4. Matching Game ```javascript function MatchingGame(items, count = 6) { const selected = shuffle(items).slice(0, count); const cards = shuffle([ ...selected.map(i => ({ ...i, type: 'japanese' })), ...selected.map(i => ({ ...i, type: 'english' })) ]); let flipped = []; let matched = []; function handleClick(index) { if (flipped.length === 2 || matched.includes(index)) return; flipped.push(index); if (flipped.length === 2) { const [a, b] = flipped; if (cards[a].japanese === cards[b].japanese && cards[a].type !== cards[b].type) { matched.push(a, b); playSound('match'); } setTimeout(() => { flipped = []; render(); }, 1000); } render(); } } ``` ### 5. Sentence Building ```javascript const SENTENCES = [ { japanese: '私は日本語を勉強しています', parts: ['私は', '日本語を', '勉強しています'], english: 'I am studying Japanese' } ]; function SentenceGame() { const sentence = SENTENCES[currentIndex]; const shuffledParts = shuffle([...sentence.parts]); let userOrder = []; function render() { return `
${sentence.english}
${shuffledParts.filter(p => !userOrder.includes(p)).map(p => ` `).join('')}
${userOrder.map((p, i) => ` `).join('')}
`; } } ``` ## Progress Tracking ```javascript const STORAGE_KEY = 'japanese-progress'; function saveProgress(type, data) { const progress = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); progress[type] = { ...progress[type], ...data, lastStudied: Date.now() }; localStorage.setItem(STORAGE_KEY, JSON.stringify(progress)); } function getProgress() { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } // Track mastery per item function updateMastery(item, correct) { const progress = getProgress(); const key = `mastery_${item}`; const current = progress[key] || { correct: 0, total: 0 }; current.total++; if (correct) current.correct++; current.mastery = current.correct / current.total; current.lastReview = Date.now(); progress[key] = current; localStorage.setItem(STORAGE_KEY, JSON.stringify(progress)); } ``` ## Keyboard Shortcuts ```javascript document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT') return; // Quiz mode - number keys for answers if (currentMode === 'quiz' && e.key >= '1' && e.key <= '4') { selectAnswer(parseInt(e.key) - 1); } // Flashcard mode if (currentMode === 'flashcard') { if (e.code === 'Space') { e.preventDefault(); flipCard(); } if (e.code === 'ArrowRight') nextCard(); if (e.code === 'ArrowLeft') prevCard(); if (e.code === 'KeyS') speak(currentCard.japanese); } // Enter to continue if (e.code === 'Enter') { handleContinue(); } }); ``` ## Mode Switching ```javascript // Prevent video from playing when in games mode function setMode(mode) { currentMode = mode; if (mode === 'games') { // Pause any playing video const iframe = document.querySelector('iframe'); if (iframe) { iframe.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*'); } } // Update UI document.querySelectorAll('.mode-tab').forEach(tab => { tab.classList.toggle('active', tab.dataset.mode === mode); }); renderCurrentMode(); } ``` ## Best Practices 1. ALWAYS use event.stopPropagation() on game UI buttons to prevent unwanted actions 2. Test speech synthesis availability before showing audio buttons 3. Provide visual feedback (color change, animation) when audio plays 4. Pause videos when switching to games mode 5. Save game progress separately from video progress 6. Include keyboard shortcuts for all games (Enter, 1-4 for choices, Space for flip) 7. Use spaced repetition for review scheduling 8. Show furigana (reading) above kanji for beginners 9. Include stroke order for kanji learning 10. Track time spent and items mastered for motivation