🤖

Kodi's Code Clubhouse

Learn HTML, CSS & JavaScript — and build your first website!

A Koinder Global Limited production

🔒

This playground is part of Kodi Premium

Unlock the full HTML Playground — puzzle blocks, live preview, sentence puzzles, the piggy bank saver, and Ada's story.

1 The Puzzle Block Library

Tap a block. It jumps into your code! 🧩

Each block is one HTML tag. Tap it, then look at Step 2 — it lands right where your cursor is blinking in the editor.

2 Build & Watch

Type on the left, see magic on the right ✨

The left box is your recipe. The right box is the cooked meal — the real webpage, exactly how a browser shows it. Change anything and watch it update instantly!

📝 My Code (the recipe)

💡 Click anywhere inside the code, then tap a Puzzle Block — it drops in right there!

🌍 My Webpage (the meal)

👀 This is what visitors on the internet would see.

🧠 How the magic works: the browser reads your name-tags one by one, like reading a shopping list. <h1>? "Ah — make it big!" <img>? "Ah — hang up a picture!" You are the boss; the tags are your instructions.

🖼️ The Picture Shelf

A picture's address goes inside src="". Tap one and Kodi will place a ready-made <img> tag into your code.

3 Sentence Puzzle

Unscramble it, then put it on your page! 🧠

Tap the word chips in the right order to build the sentence. Then send it to your webpage inside a <p> talking tag.

Word bank:
Your sentence:
4 Save it — the Piggy Bank way!

Folders are piggy banks. Files are your coins. 🐷🪙

When you save, your webpage becomes a file (a shiny coin) and drops into a folder (a piggy bank) inside your computer. Give your coin a name so you can find it again!

🏦 Inside my piggy bank:

No coins yet... save your page to fill it up!

    .html

    This piggy lives inside your computer. If your computer sleeps, your coins stay safe inside!

    ☁️ Where does the Cloud coin go? The journey!

    When you press "Send to Cloud", your coin takes a little adventure:

    💻1. Your computerThe coin starts in your piggy bank (folder).
    📡2. The internetIt zooms through wires & wifi, like a super-fast okada!
    🏢3. A data centreA giant building FULL of powerful computers that never sleep.
    ☁️🔒4. Your cloud vaultA copy of your coin sits safely there — locked with your password.
    🤫 Secret: the "cloud" is not fluffy sky-cotton — it's just someone else's very big, very strong computer in a data centre. Saving to the cloud = keeping a spare coin in a bank in town, in case anything happens to your piggy at home!
    5 Story time with Kodi

    📖 Ada and the Two Piggy Banks

    Ada built her very first webpage — a page about her cat, Whiskers, with a big <h1> title and a funny picture. She pressed Save, and clink! — her file dropped like a coin into her piggy bank folder called My-Websites, deep inside her laptop.

    But that night — poof! — the light went out, and worse, her little brother poured garri and water near the laptop! 😱 Ada's heart jumped... then she smiled. Earlier, she had also pressed "Send to Cloud." Her coin had zoomed through the internet to a giant data centre — a building full of computers that hum all day and all night, guarded like a bank vault.

    The next morning, from her mum's phone, Ada typed her password, opened her cloud vault, and there it was: whiskers-page.html, safe and shiny. She didn't lose a single word.

    🌟 Kodi's golden rule: Save your coin in your home piggy bank (your computer's folder), and keep a spare in the town bank (the cloud). Two piggy banks = zero tears! And remember — your password is the key to your vault. Never give it out!
    🪙
    `; } editor.addEventListener('input', render); render(); /* ---------------- KODI'S VOICE (text-to-speech) ---------------- */ let soundOn = true; const synth = window.speechSynthesis; let kodiVoice = null; function pickVoice(){ if(!synth) return; const vs = synth.getVoices(); kodiVoice = vs.find(v=>/en-NG/i.test(v.lang)) || vs.find(v=>/en-GB/i.test(v.lang)) || vs.find(v=>/^en/i.test(v.lang)) || vs[0] || null; } if(synth){ pickVoice(); synth.onvoiceschanged = pickVoice; } function stripEmoji(t){ return t.replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE0F}\u{2190}-\u{21FF}]/gu,'').replace(/\s+/g,' ').trim(); } let activeSayBtn = null; function speak(text, btn){ if(!soundOn || !synth) return; synth.cancel(); if(activeSayBtn){ activeSayBtn.classList.remove('speaking'); activeSayBtn = null; } const u = new SpeechSynthesisUtterance(stripEmoji(text)); if(kodiVoice) u.voice = kodiVoice; u.rate = 0.95; // a little slower, easier for kids u.pitch = 1.15; // a little brighter, friendly if(btn){ activeSayBtn = btn; btn.classList.add('speaking'); u.onend = u.onerror = ()=>{ btn.classList.remove('speaking'); if(activeSayBtn===btn) activeSayBtn=null; }; } synth.speak(u); } /* Read-this-to-me buttons: tap to hear, tap again to stop */ document.querySelectorAll('.say-btn').forEach(b=>{ b.addEventListener('click', ()=>{ if(b.classList.contains('speaking')){ synth.cancel(); b.classList.remove('speaking'); activeSayBtn=null; return; } if(!soundOn){ soundToggleBtn.click(); } speak(b.dataset.say, b); }); }); /* Master sound toggle */ const soundToggleBtn = document.getElementById('soundToggle'); soundToggleBtn.addEventListener('click', ()=>{ soundOn = !soundOn; soundToggleBtn.textContent = soundOn ? "🔊 Kodi's Voice: ON" : "🔇 Kodi's Voice: OFF"; soundToggleBtn.classList.toggle('off', !soundOn); if(!soundOn){ synth && synth.cancel(); if(activeSayBtn){activeSayBtn.classList.remove('speaking');activeSayBtn=null;} } else speak("Kodi's voice is on! Let's build together!"); }); /* Speak the little toast messages too, so every action talks */ const _oldSay = say; say = function(msg){ _oldSay(msg); speak(msg); }; /* When a puzzle block is tapped, Kodi also explains that tag out loud */ document.querySelectorAll('.block').forEach(b=>{ b.addEventListener('click', ()=>{ const desc = b.querySelector('small')?.textContent || ''; setTimeout(()=> speak('Block added! ' + desc), 80); }); }); const toast = document.getElementById('toast'); let toastTimer; function say(msg){ toast.textContent = msg; toast.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(()=>toast.classList.remove('show'), 3200); } /* ---------------- insert snippet at cursor ---------------- */ function insertSnippet(text){ const start = editor.selectionStart ?? editor.value.length; const end = editor.selectionEnd ?? editor.value.length; editor.value = editor.value.slice(0,start) + text + editor.value.slice(end); const pos = start + text.length; editor.focus(); editor.setSelectionRange(pos,pos); render(); } document.querySelectorAll('.block').forEach(b=>{ b.addEventListener('click', ()=>{ insertSnippet(b.dataset.snippet.replace(/ /g,'\n')); say('🧩 Block added! Look at your webpage →'); }); }); /* ---------------- picture shelf (offline SVG pictures) ---------------- */ const pics = [ {name:'Sunny ☀️', svg:``}, {name:'Kitty 🐱', svg:``}, {name:'Star ⭐', svg:``}, {name:'Rocket 🚀', svg:``} ]; const shelf = document.getElementById('shelf'); pics.forEach(p=>{ const uri = 'data:image/svg+xml,' + p.svg.replace(/#/g,'%23').replace(/\n/g,''); const btn = document.createElement('button'); btn.innerHTML = `${p.name}${p.name}`; btn.addEventListener('click', ()=>{ insertSnippet(`${p.name}\n`); say(`🖼️ ${p.name} is hanging on your page now!`); }); shelf.appendChild(btn); }); /* ---------------- sentence puzzle ---------------- */ const sentences = [ ['My','website','is','super','cool!'], ['I','love','puff','puff','and','code!'], ['Kodi','taught','me','HTML','today!'], ['Whiskers','the','cat','says','meow!'] ]; let target = [], built = []; const bank = document.getElementById('wordBank'); const builtBox = document.getElementById('builtSentence'); const placeBtn = document.getElementById('placeSentence'); const cheer = document.getElementById('puzzleCheer'); function shuffle(a){return a.map(v=>[Math.random(),v]).sort((x,y)=>x[0]-y[0]).map(v=>v[1]);} function newPuzzle(){ target = sentences[Math.floor(Math.random()*sentences.length)]; built = []; cheer.textContent = ''; placeBtn.disabled = true; builtBox.innerHTML = ''; bank.innerHTML = ''; shuffle([...target]).forEach(word=>{ const c = document.createElement('button'); c.className='chip'; c.textContent = word; c.addEventListener('click', ()=>{ const need = target[built.length]; if(word === need && !c.disabled){ c.disabled = true; c.style.opacity=.35; built.push(word); const done = document.createElement('span'); done.className='chip placed'; done.textContent = word; builtBox.appendChild(done); if(built.length === target.length){ cheer.textContent = '🎉 Correct! Now place it on your page!'; placeBtn.disabled = false; speak('Correct! Well done! Now press place it on my page!'); } } else { cheer.textContent = '🤔 Hmm, not that one yet — which word comes next?'; speak('Hmm, not that one yet. Which word comes next?'); } }); bank.appendChild(c); }); } document.getElementById('shuffleBtn').addEventListener('click', newPuzzle); placeBtn.addEventListener('click', ()=>{ insertSnippet(`

    ⭐ ${built.join(' ')}

    \n`); say('✍️ Your sentence is on the webpage — in a

    talking tag!'); newPuzzle(); }); newPuzzle(); /* ---------------- piggy bank saving ---------------- */ const files = []; // in-memory only, like a real play session const fileList = document.getElementById('fileList'); const emptyMsg = document.getElementById('emptyMsg'); const coin = document.getElementById('coin'); const piggyZone = document.getElementById('piggyZone'); const piggyLabel = document.getElementById('piggyLabel'); const piggyPick = document.getElementById('piggyPick'); piggyPick.addEventListener('change', ()=>{ piggyLabel.textContent = piggyPick.value.replace(' 🐷',''); }); document.getElementById('saveBtn').addEventListener('click', ()=>{ let name = document.getElementById('fname').value.trim() || 'my-page'; if(!name.toLowerCase().endsWith('.html')) name += '.html'; const folder = piggyPick.value.replace(' 🐷',''); // coin drop animation coin.classList.remove('drop'); void coin.offsetWidth; coin.classList.add('drop'); piggyZone.classList.remove('happy'); void piggyZone.offsetWidth; piggyZone.classList.add('happy'); const file = {name, folder, cloud:false, code:editor.value}; files.push(file); drawFiles(); say(`🪙 Clink! "${name}" saved inside the ${folder} piggy bank!`); }); function drawFiles(){ emptyMsg.style.display = files.length ? 'none' : 'block'; fileList.innerHTML = ''; files.forEach((f,i)=>{ const li = document.createElement('li'); li.innerHTML = `🪙 ${f.name} ${f.cloud ? '☁️ computer + cloud' : '💻 '+f.folder}`; if(!f.cloud){ const cb = document.createElement('button'); cb.className='cloud-btn'; cb.textContent='☁️ Send to Cloud'; cb.addEventListener('click',(e)=> sendToCloud(i, e.target)); li.appendChild(cb); } fileList.appendChild(li); }); } const flyer = document.getElementById('flyer'); function sendToCloud(i, fromEl){ const r = fromEl.getBoundingClientRect(); flyer.style.display='block'; flyer.style.transition='none'; flyer.style.transform=`translate(${r.left}px, ${r.top}px) scale(1)`; flyer.style.opacity='1'; requestAnimationFrame(()=>{ requestAnimationFrame(()=>{ flyer.style.transition='transform 1.4s cubic-bezier(.45,-0.2,.4,1.1), opacity 1.4s'; flyer.style.transform=`translate(${window.innerWidth-90}px, 30px) scale(.4) rotate(360deg)`; flyer.style.opacity='0'; }); }); setTimeout(()=>{ flyer.style.display='none'; }, 1500); files[i].cloud = true; setTimeout(()=>{ drawFiles(); say(`☁️ Zoom! "${files[i].name}" flew to the data centre. Now you have a spare coin in the town bank — safe even if NEPA takes the light! 🔒`); }, 1200); }

    🎉 You've built a page, saved it like a coin, and learned where files really live!

    Ready to give your page some style and colour?

    🎨 Next: CSS — Bobo's Clothes →
    🏅

    An Official Koinder Skills Academy Programme

    Kodi's Code Clubhouse is produced by Koinder Global Limited. Koinder Global Limited is licensed by the FCT Department of Mass Education to provide vocational skills training and run Non-Formal Education programmes.

    Lead Tutor & Curriculum Developer: Glory N. Nweke