DEV Community

PROBOYIZ
PROBOYIZ

Posted on

PROCLUB

PROCLUB IS A LEARNING APP FOR 5-10-YEAR-OLDS. THIS IS THE CODE FOR THAT:

<!DOCTYPE html>









PROCLUB





<br>
/* General Reset &amp; Base Styles <em>/<br>
body, html {<br>
margin: 0;<br>
padding: 0;<br>
height: auto; /</em> Allow content to dictate height <em>/<br>
min-height: 100vh; /</em> Ensure it takes at least full viewport height <em>/<br>
overflow-y: auto; /</em> Enable vertical scrolling if content overflows /<br>
font-family: &#39;Arial Black&#39;, Arial, sans-serif;<br>
color: white;<br>
text-transform: uppercase;<br>
display: flex;<br>
flex-direction: column;<br>
}</p>
<div class="highlight"><pre class="highlight plaintext"><code> /
Full RGB Background Glow /
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: linear-gradient(45deg, #FF007F, #00FFD1, #FFD100, #7F00FF); /
Pink, Cyan, Yellow, Purple /
background-size: 400% 400%; /
Make the gradient large for smooth movement /
animation: rgbAnimate 15s ease infinite alternate; /
Slow animation /
opacity: 0.8; /
Make it more vibrant */
}
@keyframes rgbAnimate {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

/* Navigation Button */
#navBtn {
    position: fixed;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    padding: 10px 25px;
    font-size: 1.2em;
    cursor: pointer;
    border: 2px solid #0ff;
    border-radius: 12px;
    background: rgba(0,255,255,0.3);
    color: white;
    text-transform: uppercase;
    z-index: 9999;
    box-shadow: 0 0 15px #0ff;
    transition: background 0.3s ease;
}
#navBtn:hover {
    background: rgba(0,255,255,0.6);
}

/* Page Container &amp;amp; Page Styling */
#pagesContainer {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    padding-top: 50px; /* To account for fixed navBtn */
    padding-bottom: 20px; /* Add some padding at the bottom for scrolling */
}
.page {
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    flex-direction: column;
    padding: 20px; /* Add padding to pages themselves */
    box-sizing: border-box; /* Include padding in element's total width and height */
}

/* Home Page Specific Styles */
#homePage h1 {
    font-size: 4.5em; /* Slightly smaller for multi-line text */
    margin-bottom: 20px;
    text-shadow: 0 0 20px #fff, 0 0 40px #0ff, 0 0 60px #f0f;
    line-height: 1.2;
}
#homePage p {
    font-size: 1.5em; /* Slightly smaller for multi-line text */
    text-shadow: 0 0 10px rgba(255,255,255,0.7);
    max-width: 80%; /* To prevent very long lines */
    margin-bottom: 20px;
}
#homePage .instructions-paragraph {
    font-size: 1.1em; /* Adjusted size for the instructions */
    max-width: 70%; /* Narrower for better readability of a large block of text */
    line-height: 1.6;
    text-align: left; /* Align left for paragraph */
    margin-top: 20px;
    border: 2px solid rgba(255,255,255,0.5);
    padding: 20px;
    background: rgba(0,0,0,0.3); /* Slightly dark background for contrast */
    border-radius: 10px;
    box-shadow: 0 0 15px rgba(255,255,255,0.3);
}
#homePage .instructions-paragraph ul {
    list-style: none; /* Remove default bullet points */
    padding: 0;
    margin: 0;
}
#homePage .instructions-paragraph li {
    margin-bottom: 15px; /* Add space between list items (facts) */
    padding-bottom: 5px; /* Add a little extra space */
    border-bottom: 1px dotted rgba(255,255,255,0.2); /* Optional: subtle separator */
}
#homePage .instructions-paragraph li:last-child {
    margin-bottom: 0;
    border-bottom: none;
}

/* Music Toggle Button */
#musicToggleBtn {
    margin-top: 20px;
    padding: 10px 20px;
    font-size: 1em;
    cursor: pointer;
    border: 2px solid #ffd100; /* Yellow border */
    border-radius: 10px;
    background: rgba(255,210,0,0.3); /* Semi-transparent yellow */
    color: white;
    text-transform: uppercase;
    box-shadow: 0 0 15px #ffd100;
    transition: background 0.3s ease;
}
#musicToggleBtn:hover {
    background: rgba(255,210,0,0.6);
}

/* Timer page buttons */
.timer-btn {
    padding: 15px 40px;
    font-size: 20px;
    font-weight: bold;
    cursor: pointer;
    border: none;
    border-radius: 8px;
    background-color: white;
    color: black;
    text-transform: uppercase;
    box-shadow: 0 0 10px #00ffff;
    margin: 0 20px;
    transition: background 0.3s ease;
}
.timer-btn:hover {
    background-color: #00ffff;
    color: white;
}

/* Pixel Creator specific styles */
#pixelCanvas {
    background: white;
    border: 4px solid #000;
    image-rendering: pixelated;
    cursor: crosshair;
}
#pixelCreatorToolbar {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    margin-bottom: 20px;
    z-index: 2;
}
#pixelCreatorToolbar label {
    color: white;
}
#pixelCreatorToolbar input[type="color"] {
    width: 40px;
    height: 40px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}
#pixelCreatorToolbar input[type="text"] {
    padding: 10px;
    font-size: 1rem;
    border-radius: 6px;
    border: 2px solid #aaa;
    text-transform: uppercase;
}
#pixelCreatorToolbar button {
    padding: 10px 25px;
    font-size: 1.2em;
    cursor: pointer;
    border: 2px solid #0ff;
    border-radius: 12px;
    background: rgba(0,255,255,0.3);
    color: white;
    text-transform: uppercase;
    box-shadow: 0 0 15px #0ff;
    transition: background 0.3s ease;
}
#pixelCreatorToolbar button:hover {
    background: rgba(0,255,255,0.6);
}

/* Input field consistency */
input[type="text"]#typingInput,
input[type="number"]#mathAnswerInput {
    padding: 10px;
    font-size: 24px;
    text-transform: uppercase;
    width: 80%;
    max-width: 600px;
    text-align: center;
    background: #000;
    color: #0f0;
    border: 2px solid #0f0;
    outline: none;
}

/* Math Game Specific Styles */
#mathQuestion {
    font-size: 3em;
    margin-bottom: 20px;
    color: white;
    text-shadow: 0 0 15px #fff, 0 0 30px #0ff, 0 0 45px #f0f;
}
#mathFeedback {
    margin-top: 15px;
    font-size: 1.5em;
    color: #0f0;
    text-shadow: 0 0 10px #0f0;
    min-height: 1.8em;
}
#mathScore {
    font-size: 1.8em;
    margin-top: 20px;
    color: white;
    text-shadow: 0 0 10px #0ff;
}
.math-operation-btn {
    padding: 12px 30px;
    font-size: 1.3em;
    cursor: pointer;
    border: 2px solid #ff0;
    border-radius: 10px;
    background: rgba(255,255,0,0.3);
    color: white;
    text-transform: uppercase;
    box-shadow: 0 0 10px #ff0;
    transition: background 0.3s ease, transform 0.2s;
    margin: 0 10px;
    margin-top: 30px;
}
.math-operation-btn:hover {
    background: rgba(255,255,0,0.6);
    transform: scale(1.05);
}

/* Voice Creator Specific Styles */
#voiceCreatorPage textarea {
    width: 80%;
    max-width: 700px;
    height: 200px;
    padding: 15px;
    font-size: 1.5em;
    border: 3px solid #ffd100; /* Yellow border */
    border-radius: 15px;
    background: rgba(0,0,0,0.5); /* Semi-transparent dark background */
    color: #0f0; /* Green text for contrast */
    resize: vertical; /* Allow vertical resizing */
    outline: none;
    box-shadow: 0 0 20px #ffd100; /* Yellow shadow */
    margin-bottom: 20px;
}
#voiceCreatorPage .voice-btn {
    padding: 15px 30px;
    font-size: 1.2em;
    cursor: pointer;
    border: 2px solid #ffd100; /* Yellow border */
    border-radius: 15px;
    background: rgba(255,210,0,0.3); /* Semi-transparent yellow */
    color: white;
    text-transform: uppercase;
    box-shadow: 0 0 20px #ffd100; /* Yellow shadow */
    transition: background 0.3s ease, transform 0.2s;
    margin: 0 10px 15px; /* Margin bottom for spacing */
}
#voiceCreatorPage .voice-btn:hover {
    background: rgba(255,210,0,0.6); /* Darker yellow on hover */
    transform: scale(1.05);
}
#voiceFeedback {
    margin-top: 10px;
    font-size: 1.2em;
    color: #0ff; /* Cyan for feedback */
    text-shadow: 0 0 10px #0ff;
    min-height: 1.5em; /* Reserve space */
}

/* CUBS Page (New Addition) */
#cubsPage {
    position: relative;
    background: rgba(0,0,0,0.5);
    border-radius: 15px;
    padding: 20px;
    box-shadow: 0 0 30px rgba(255,255,255,0.5);
    width: 90%;
    max-width: 800px;
    margin: 20px auto;
    min-height: 400px; /* Minimum height for the 3D scene */
    display: flex; /* Ensure it's a flex container */
    flex-direction: column; /* Stack children vertically */
    align-items: center; /* Center items horizontally */
    justify-content: flex-start; /* Align items to the start vertically */
}

#cubsPage h1 {
    font-size: 3em;
    margin-bottom: 20px;
    text-shadow: 0 0 15px #fff, 0 0 30px #0f0, 0 0 45px #00f; /* Green and Blue glow */
}

#cubsCanvas {
    width: 100% !important; /* Ensure canvas takes full width of parent */
    height: 400px; /* Fixed height for the 3D scene */
    display: block; /* Remove extra space below canvas */
    margin-top: 20px;
    border: 2px solid #0f0;
    border-radius: 8px;
    box-shadow: 0 0 15px rgba(0,255,0,0.7);
}

#cubsControls {
    margin-top: 20px;
    display: flex;
    gap: 15px;
    flex-wrap: wrap;
    justify-content: center;
}

.cubs-btn {
    padding: 12px 25px;
    font-size: 1.1em;
    cursor: pointer;
    border: 2px solid #0f0; /* Green border */
    border-radius: 10px;
    background: rgba(0,255,0,0.3);
    color: white;
    text-transform: uppercase;
    box-shadow: 0 0 10px #0f0;
    transition: background 0.3s ease, transform 0.2s;
}
.cubs-btn:hover {
    background: rgba(0,255,0,0.6);
    transform: scale(1.05);
}
Enter fullscreen mode Exit fullscreen mode

&lt;/style&gt;
</code></pre></div>
<p></head><br>
<body></p>
<div class="highlight"><pre class="highlight plaintext"><code>&lt;button id="navBtn"&gt;GO TO WORD CYCLE&lt;/button&gt;
&lt;div id="pagesContainer"&gt;&lt;/div&gt;

&lt;script&gt;
// SLOW SMOOTH RGB BACKGROUND (Unified)
let i = 0;
setInterval(() =&gt; {
const r = Math.floor(128 + 128 * Math.sin(i));
const g = Math.floor(128 + 128 * Math.sin(i + 2));
const b = Math.floor(128 + 128 * Math.sin(i + 4));
document.body.style.backgroundColor = rgb(${r},${g},${b});
i += 0.005;
}, 50);

const navBtn = document.getElementById('navBtn');
const pagesContainer = document.getElementById('pagesContainer');

// --- PAGE 0: HOME PAGE ---
const page0 = document.createElement("div");
page0.classList.add('page');
page0.id = 'homePage';
page0.style.display = "flex";

const homeTitle = document.createElement("h1");
homeTitle.textContent = "WELCOME TO PROCLUB";
page0.appendChild(homeTitle);

const homeTagline = document.createElement("p");
homeTagline.textContent = "YOU CAN EXPLORE OTHER PAGES BY CLICKING THE BUTTON AT THE TOP. THIS WEBSITE IS FOR 5-10 YEAR OLDS.";
page0.appendChild(homeTagline);

// Audio element for background music
const backgroundMusic = document.createElement("audio");
backgroundMusic.id = "backgroundMusic";
backgroundMusic.src = "embrace-364091.mp3"; // Path to your music file
backgroundMusic.autoplay = true; // Attempt autoplay
backgroundMusic.loop = true;
backgroundMusic.volume = 0.3; // Set initial volume
backgroundMusic.controls = true; // Show controls
page0.appendChild(backgroundMusic);

// Instructions Paragraph
const instructionsParagraph = document.createElement("p");
instructionsParagraph.classList.add("instructions-paragraph");
instructionsParagraph.innerHTML = `
    &amp;lt;h3&amp;gt;HOW TO USE PROCLUB:&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;Welcome to PROCLUB, your fun and interactive learning hub! Here's a quick guide on how to get the most out of your experience:&amp;lt;/p&amp;gt;
    &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;NAVIGATE WITH THE BUTTON:&amp;lt;/strong&amp;gt; Look for the big button at the top of the page. It will say "GO TO [PAGE NAME]". Click it to jump to different sections of the website.&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;WORD CYCLE:&amp;lt;/strong&amp;gt; On this page, you'll see a word. Click on the word to change it to another random word. It's great for practicing reading and discovering new words!&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;FEELINGS TRACKER:&amp;lt;/strong&amp;gt; Select how you are feeling from the buttons provided (Happy, Sad, Excited, Calm, Angry). After you choose, a pop-up will ask you to share why you feel that way. It's a great way to think about your emotions!&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;TYPING TEST:&amp;lt;/strong&amp;gt; Challenge your typing skills! Click "START" and then type the word you see on the screen as fast as you can. Your score and level will increase as you type correctly.&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;TIMER:&amp;lt;/strong&amp;gt; This is a simple stopwatch. Click "START" to begin timing, "STOP" to pause, and "RESET" to clear the timer. Perfect for timing your games or activities!&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;PIXEL CREATOR:&amp;lt;/strong&amp;gt; Unleash your inner artist! Choose a color and click or drag on the canvas to draw pixel art. You can clear your drawing or save it to your device. Don't forget to enter your name!&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;BRAIN BLASTER:&amp;lt;/strong&amp;gt; This is our super fun math game! Choose an operation (Addition, Subtraction, Multiplication, or Division) and solve the math problems. Type your answer and press Enter. Get points for correct answers and become a math master!&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;VOICE CREATOR:&amp;lt;/strong&amp;gt; Type any words you like and have the app speak them aloud! You can also clear, undo, and redo your text. The "SAVE TEXT" button will download your typed words as a text file.&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;CUBS (3D FUN):&amp;lt;/strong&amp;gt; Explore an interactive 3D scene with adorable cubs! You can change their color, and rotate the camera. Have fun with our furry friends!&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
    &amp;lt;p&amp;gt;Remember, PROCLUB is designed for kids aged 5-10, so everything is made to be easy and enjoyable. Have fun exploring and learning!&amp;lt;/p&amp;gt;
`;
page0.appendChild(instructionsParagraph);

pagesContainer.appendChild(page0);

// --- PAGE 1: WORD CYCLE ---
const page1 = document.createElement("div");
page1.classList.add('page');
page1.style.textAlign = "center";
page1.style.display = "none";

const glowingText = document.createElement("div");
glowingText.innerText = "READY TO TRY OUT OUR APP CLICK ON A WORD TO CHANGE IT!";
Object.assign(glowingText.style, {
    fontSize: "2.5em",
    cursor: "pointer",
    textShadow: "0 0 20px #fff, 0 0 40px #0ff, 0 0 60px #f0f",
    whiteSpace: "nowrap",
    userSelect: "none",
});
page1.appendChild(glowingText);

const words = [
    "TRANQUILLITY", "PEACE", "CALM", "BREATHE", "BREATHE OUT", "RELEX", "HELLO", "HI", "YES", "NO",
    "BRO", "MAN", "WOMAN", "BOY", "GIRL", "THANKS", "PLEASE", "FRIEND", "SCHOOL", "WORK",
    "LOVE", "HAPPY", "SORRY", "GOOD", "BAD", "NICE", "COOL", "SMART", "STRONG", "WEAK",
    "HOME", "HOUSE", "GAME", "FUN", "FAMILY", "TEACHER", "STUDENT", "COMPUTER", "TABLE", "WATER",
    "FOOD", "ANIMAL", "MUSIC", "SONG", "NIGHT", "MORNING", "DREAM", "LAUGH", "SIMLE", "CLOUD",
    "WOULD", "PLAY", "PLAYING", "DAY", "THINK", "TIME", "LOOK", "MAKE", "MADE", "CALL",
    "I", "DON'T", "DO NOT", "DO", "IT", "HELP", "WANT", "COME", "BACK", "GIVE",
    "TAKE", "FIND", "TELL", "FEEL", "NOW", "RAINY", "SUNNY", "STAR", "ERATH", "MOON",
    "SUN", "MARS", "WALK", "RUN", "SLEEP", "READ", "WRITE", "LISTEN", "WATCH", "SPEAK",
    "JUMP", "SIT", "OPEN", "CLOSE", "START", "STOP", "MINECRAFT", "ROBLOX", "GTA 5", "PUBG",
    "FREEFIRE", "WAIT", "STAND", "CARRY", "DRIVE", "CLEAN", "DRAW", "WAKE", "SMILE", "CRY",
    "SHOUT", "TOUCH", "DIRTY", "COLD", "HOT", "RIDE", "BEGIN", "END", "MOVE", "FORTNITE",
    "SUCKS", "DOES", "AND", "BUT", "OR", "IF", "IS", "IT", "AT", "ON",
    "IN", "BY", "TO", "AS", "AN", "SO", "UP", "NO", "MY", "OF",
    "BE", "DO", "GO", "HE", "ME", "WE", "US", "AM", "YOU", "THE",
    "THEN", "WHY", "CAN", "DID", "HAD", "HAS", "HER", "HIM", "HIS", "ITS",
    "LET", "MAY", "MUST", "NOT", "NOW", "OFF", "OLD", "OUR", "OUT", "SEE",
    "SHE", "THAN", "THAT", "THEY", "THIS", "THOSE", "TOO", "VERY", "WAS", "WAY",
    "WHO", "WILL", "TEETH", "WAKE UP", "BYE", "CELEBRATION", "CREATION", "ADVENTURE", "PHILOSOPHY",
    "GENERATION", "ORGANIZATION", "OPPORTUNITY", "INCREDIBLE", "CHALLENGE", "COMMUNITY", "BEAUTIFUL",
    "RELATIONSHIP", "ENVIRONMENT", "INSPIRATION", "TECHNOLOGY", "INDEPENDENT", "MOTIVATION", "PERSONALITY",
    "IMPROVEMENT", "COMMUNICATION", "DIRECTION", "EXPERIENCE", "FLEXIBILITY", "IMAGINATION", "KINDNESS",
    "LEADERSHIP", "NECESSARY", "POSITIVE", "RESPONSIBLE", "IMPORTANT", "DISCUSSION", "DEVELOPMENT",
    "RECOGNITION", "UNDERSTANDING", "PERFORMANCE", "COMPARISON", "APPEARANCE", "EXPERIMENT", "EXPLANATION",
    "RECOMMENDATION", "ESTABLISHMENT", "CONNECTION", "INVITATION", "TRANSLATION", "INFORMATION", "DIFFERENCE",
    "EXERCISE", "EXPRESSION", "CONVERSATION", "POPULATION", "CONSTRUCTION", "INTRODUCTION", "COMPETITION",
    "COMPREHENSIVE", "EXPERIENCED", "CONSEQUENCE", "SIGNIFICANT", "SITUATION", "APPLICATION", "CONDITION",
    "EDUCATION", "DISTRIBUTION", "INTERNATIONAL", "MANAGEMENT", "REPRESENTATIVE", "ECONOMICAL", "ABANDON",
    "ABATE", "ADMINISTRATION", "ABLE", "ABILITY", "ABNORMAL", "ABODE", "ABHOR", "ABIDE", "ABOUT", "ABOVE",
    "ABRUPT", "ABSTAIN", "ABYSS", "ACCEPT", "ACCESS", "ABUSE", "ACUTE", "ADDRESS", "ADMIRE", "ADMIT",
    "ADULT", "ADORN", "FIX", "AGAIN", "AGE", "AGILE"
];

let currentWordIndex = 0;
glowingText.addEventListener("click", () =&amp;gt; {
    currentWordIndex = (currentWordIndex + 1) % words.length;
    glowingText.innerText = words[currentWordIndex];
    glowingText.style.fontSize = "4em";
});

let floatAngle = 0;
setInterval(() =&amp;gt; {
    const y = Math.sin(floatAngle) * 10;
    glowingText.style.transform = `translateY(${y}px)`;
    floatAngle += 0.05;
}, 30);

pagesContainer.appendChild(page1);

// --- PAGE 2: FEELING TRACKER ---
const page2 = document.createElement("div");
page2.classList.add('page');
page2.style.display = "none";
page2.style.flexDirection = "column";
page2.style.alignItems = "center";

const title2 = document.createElement("h1");
title2.textContent = "HOW ARE YOU FEELING TODAY?";
title2.style.marginBottom = "40px";
title2.style.textShadow = "0 0 10px #0ff";
page2.appendChild(title2);

const feelingsContainer = document.createElement("div");
feelingsContainer.style.display = "flex";
feelingsContainer.style.gap = "20px";
feelingsContainer.style.flexWrap = "wrap";
feelingsContainer.style.justifyContent = "center";
page2.appendChild(feelingsContainer);

const feelingsList = ["HAPPY", "SAD", "EXCITED", "CALM", "ANGRY"];

feelingsList.forEach(feeling =&amp;gt; {
    const btn = document.createElement("button");
    btn.textContent = feeling;
    Object.assign(btn.style, {
        background: "rgba(0,255,255,0.3)",
        border: "2px solid #0ff",
        borderRadius: "12px",
        color: "white",
        padding: "15px 25px",
        fontSize: "1.5em",
        cursor: "pointer",
        boxShadow: "0 0 15px #0ff",
        transition: "background 0.3s, transform 0.3s",
        textTransform: "uppercase",
    });
    btn.addEventListener("mouseenter", () =&amp;gt; {
        btn.style.background = "rgba(0,255,255,0.6)";
        btn.style.transform = "scale(1.1)";
    });
    btn.addEventListener("mouseleave", () =&amp;gt; {
        btn.style.background = "rgba(0,255,255,0.3)";
        btn.style.transform = "scale(1)";
    });
    btn.addEventListener("click", () =&amp;gt; {
        const reason = prompt(`WHY ARE YOU FEELING ${feeling}?`)?.toUpperCase().trim();
        if (reason) {
            console.log(`User is feeling ${feeling} because ${reason}`);
        } else {
            console.log(`User did not provide a reason for feeling ${feeling}`);
        }
    });
    feelingsContainer.appendChild(btn);
});
pagesContainer.appendChild(page2);

// --- PAGE 3: TYPING TEST ---
const page3 = document.createElement("div");
page3.classList.add('page');
page3.style.display = "none";
page3.style.flexDirection = "column";
page3.style.alignItems = "center";

const title3 = document.createElement("h1");
title3.textContent = "TYPING TEST";
title3.style.color = "white";
title3.style.textShadow = "0 0 10px #fff, 0 0 20px #0ff";
page3.appendChild(title3);

const typingWord = document.createElement("div");
typingWord.id = "typingWord";
Object.assign(typingWord.style, {
    fontSize: "40px",
    letterSpacing: "4px",
    margin: "20px",
    color: "white",
    textShadow: "0 0 10px #fff, 0 0 20px #0ff, 0 0 30px #0ff",
});
typingWord.textContent = "PRESS START";
page3.appendChild(typingWord);

const typingInput = document.createElement("input");
    typingInput.id = "typingInput";
    typingInput.type = "text";
    typingInput.placeholder = "TYPE HERE...";
    typingInput.disabled = true;
    page3.appendChild(typingInput);

const infoBar = document.createElement("div");
infoBar.id = "infoBar";
infoBar.style.marginTop = "20px";
infoBar.style.fontSize = "18px";
infoBar.style.color = "white";
infoBar.innerHTML = `
    LEVEL: &amp;lt;span id="level"&amp;gt;0&amp;lt;/span&amp;gt; &amp;amp;nbsp;&amp;amp;nbsp;
    SCORE: &amp;lt;span id="score"&amp;gt;0&amp;lt;/span&amp;gt; &amp;amp;nbsp;&amp;amp;nbsp;
    TIME: &amp;lt;span id="time"&amp;gt;0&amp;lt;/span&amp;gt; SEC
`;
page3.appendChild(infoBar);

const typedWordsDiv = document.createElement("div");
typedWordsDiv.id = "typedWords";
Object.assign(typedWordsDiv.style, {
    marginTop: "20px",
    width: "80%",
    minHeight: "80px",
    border: "2px solid #0ff",
    borderRadius: "8px",
    padding: "10px",
    color: "white",
    textShadow: "0 0 8px #fff, 0 0 12px #0ff",
    fontSize: "20px",
    textTransform: "uppercase",
    display: "flex",
    flexWrap: "wrap",
    gap: "10px",
    justifyContent: "center",
});
page3.appendChild(typedWordsDiv);

const startBtnTyping = document.createElement("button");
startBtnTyping.id = "startBtnTyping";
startBtnTyping.textContent = "START";
Object.assign(startBtnTyping.style, {
    marginTop: "20px",
    padding: "10px 20px",
    fontSize: "18px",
    cursor: "pointer",
    background: "rgba(0,255,255,0.3)",
    color: "white",
    border: "2px solid #0ff",
    borderRadius: "8px",
    textTransform: "uppercase",
    textShadow: "0 0 10px #0ff",
    transition: "background 0.3s ease",
});
startBtnTyping.onmouseenter = () =&amp;gt; {
    if (!startBtnTyping.disabled) startBtnTyping.style.background = "rgba(0,255,255,0.6)";
};
startBtnTyping.onmouseleave = () =&amp;gt; {
    if (!startBtnTyping.disabled) startBtnTyping.style.background = "rgba(0,255,255,0.3)";
};
page3.appendChild(startBtnTyping);
pagesContainer.appendChild(page3);

// --- PAGE 4: TIMER ---
const page4 = document.createElement("div");
page4.classList.add('page');
page4.style.display = "none";
page4.style.flexDirection = "column";
page4.style.alignItems = "center";

const timerDisplay = document.createElement("div");
timerDisplay.textContent = "00:00";
Object.assign(timerDisplay.style, {
    fontSize: "12vw",
    color: "white",
    textShadow: `
        0 0 5px white,
        0 0 10px white,
        0 0 15px #00ffff,
        0 0 20px #00ffff
    `,
    userSelect: "none",
    marginBottom: "40px",
});
page4.appendChild(timerDisplay);

const timerButtons = document.createElement("div");
timerButtons.style.display = "flex";
timerButtons.style.justifyContent = "center";
timerButtons.style.gap = "40px";
page4.appendChild(timerButtons);

const timerStartBtn = document.createElement("button");
timerStartBtn.textContent = "START";
timerStartBtn.className = "timer-btn";
timerButtons.appendChild(timerStartBtn);

const timerStopBtn = document.createElement("button");
timerStopBtn.textContent = "STOP";
timerStopBtn.className = "timer-btn";
timerButtons.appendChild(timerStopBtn);

const timerResetBtn = document.createElement("button");
timerResetBtn.textContent = "RESET";
timerResetBtn.className = "timer-btn";
timerButtons.appendChild(timerResetBtn);
pagesContainer.appendChild(page4);

// --- PAGE 5: PIXEL CREATOR ---
const page5 = document.createElement("div");
page5.classList.add('page');
page5.style.display = "none";
page5.style.flexDirection = "column";
page5.style.alignItems = "center";

const pixelCreatorToolbar = document.createElement("div");
pixelCreatorToolbar.id = "pixelCreatorToolbar";
pixelCreatorToolbar.innerHTML = `
    &amp;lt;label&amp;gt;COLOR: &amp;lt;input type="color" id="colorPicker" /&amp;gt;&amp;lt;/label&amp;gt;
    &amp;lt;button id="clearCanvasBtn"&amp;gt;CLEAR&amp;lt;/button&amp;gt;
    &amp;lt;button id="saveImageBtn"&amp;gt;SAVE&amp;lt;/button&amp;gt;
    &amp;lt;input type="text" id="playerName" placeholder="PLAYER NAME" /&amp;gt;
`;
page5.appendChild(pixelCreatorToolbar);

const pixelCanvas = document.createElement("canvas");
pixelCanvas.id = "pixelCanvas";
pixelCanvas.width = 512;
pixelCanvas.height = 512;
page5.appendChild(pixelCanvas);
pagesContainer.appendChild(page5);

// --- PAGE 6: MATH GAME (BRAIN BLASTER) ---
const page6 = document.createElement("div");
page6.classList.add('page');
page6.style.display = "none";
page6.style.flexDirection = "column";
page6.style.alignItems = "center";

const mathTitle = document.createElement("h1");
mathTitle.textContent = "BRAIN BLASTER";
mathTitle.style.color = "white";
mathTitle.style.textShadow = "0 0 10px #fff, 0 0 20px #ff0";
page6.appendChild(mathTitle);

const mathQuestion = document.createElement("div");
mathQuestion.id = "mathQuestion";
mathQuestion.textContent = "SELECT AN OPERATION TO START!";
page6.appendChild(mathQuestion);

const mathAnswerInput = document.createElement("input");
mathAnswerInput.id = "mathAnswerInput";
mathAnswerInput.type = "number";
mathAnswerInput.placeholder = "TYPE YOUR ANSWER HERE...";
mathAnswerInput.disabled = true;
page6.appendChild(mathAnswerInput);

const mathFeedback = document.createElement("div");
mathFeedback.id = "mathFeedback";
mathFeedback.textContent = "";
page6.appendChild(mathFeedback);

const mathScore = document.createElement("div");
mathScore.id = "mathScore";
mathScore.textContent = "SCORE: 0";
page6.appendChild(mathScore);

const operationButtonsContainer = document.createElement("div");
operationButtonsContainer.style.marginTop = "30px";
page6.appendChild(operationButtonsContainer);

const operations = [
    { symbol: "+", name: "ADDITION" },
    { symbol: "-", name: "SUBTRACTION" },
    { symbol: "*", name: "MULTIPLICATION" },
    { symbol: "/", name: "DIVISION" }
];

operations.forEach(op =&amp;gt; {
    const opBtn = document.createElement("button");
    opBtn.textContent = op.name;
    opBtn.className = "math-operation-btn";
    opBtn.dataset.operation = op.symbol;
    operationButtonsContainer.appendChild(opBtn);
});

pagesContainer.appendChild(page6);

// --- PAGE 7: VOICE CREATOR ---
const voiceCreatorPage = document.createElement("div");
voiceCreatorPage.classList.add('page');
voiceCreatorPage.id = 'voiceCreatorPage';
voiceCreatorPage.style.display = "none";

const voiceCreatorTitle = document.createElement("h1");
voiceCreatorTitle.textContent = "VOICE CREATOR";
voiceCreatorTitle.style.color = "white";
voiceCreatorTitle.style.textShadow = "0 0 10px #fff, 0 0 20px #ffd100"; /* Yellow shadow for title */
voiceCreatorPage.appendChild(voiceCreatorTitle);

const voiceInput = document.createElement("textarea");
voiceInput.id = "voiceInput";
voiceInput.placeholder = "TYPE YOUR WORDS HERE...";
voiceCreatorPage.appendChild(voiceInput);

const voiceFeedback = document.createElement("div");
voiceFeedback.id = "voiceFeedback";
voiceCreatorPage.appendChild(voiceFeedback);

const voiceControls = document.createElement("div");
voiceCreatorPage.appendChild(voiceControls);

const playVoiceBtn = document.createElement("button");
playVoiceBtn.textContent = "PLAY VOICE";
playVoiceBtn.classList.add("voice-btn");
voiceControls.appendChild(playVoiceBtn);

const clearVoiceBtn = document.createElement("button");
clearVoiceBtn.textContent = "CLEAR TYPING AREA";
clearVoiceBtn.classList.add("voice-btn");
voiceControls.appendChild(clearVoiceBtn);

const saveTextBtn = document.createElement("button");
saveTextBtn.textContent = "SAVE TEXT";
saveTextBtn.classList.add("voice-btn");
voiceControls.appendChild(saveTextBtn);

const undoVoiceBtn = document.createElement("button");
undoVoiceBtn.textContent = "UNDO";
undoVoiceBtn.classList.add("voice-btn");
voiceControls.appendChild(undoVoiceBtn);

const redoVoiceBtn = document.createElement("button");
redoVoiceBtn.textContent = "REDO";
redoVoiceBtn.classList.add("voice-btn");
voiceControls.appendChild(redoVoiceBtn);

pagesContainer.appendChild(voiceCreatorPage);

// --- PAGE 8: CUBS (3D Scene) ---
const cubsPage = document.createElement("div");
cubsPage.classList.add('page');
cubsPage.id = 'cubsPage';
cubsPage.style.display = "none";

const cubsTitle = document.createElement("h1");
cubsTitle.textContent = "INTERACTIVE CUBS!";
cubsPage.appendChild(cubsTitle);

const cubsCanvas = document.createElement("canvas");
cubsCanvas.id = "cubsCanvas";
cubsPage.appendChild(cubsCanvas);

const cubsControls = document.createElement("div");
cubsControls.id = "cubsControls";
cubsPage.appendChild(cubsControls);

const cubsColorPicker = document.createElement("input");
cubsColorPicker.type = "color";
cubsColorPicker.id = "cubsColorPicker";
cubsColorPicker.value = "#ffa500"; // Default orange color
cubsControls.appendChild(cubsColorPicker);

// Removed the JUMP button as per user's request
// const cubsJumpBtn = document.createElement("button");
// cubsJumpBtn.textContent = "JUMP!";
// cubsJumpBtn.classList.add("cubs-btn");
// cubsJumpBtn.id = "cubsJumpBtn";
// cubsControls.appendChild(cubsJumpBtn);

const cubsRotateLeftBtn = document.createElement("button");
cubsRotateLeftBtn.textContent = "ROTATE LEFT";
cubsRotateLeftBtn.classList.add("cubs-btn");
cubsRotateLeftBtn.id = "cubsRotateLeftBtn";
cubsControls.appendChild(cubsRotateLeftBtn);

const cubsRotateRightBtn = document.createElement("button");
cubsRotateRightBtn.textContent = "ROTATE RIGHT";
cubsRotateRightBtn.classList.add("cubs-btn");
cubsRotateRightBtn.id = "cubsRotateRightBtn";
cubsControls.appendChild(cubsRotateRightBtn);

pagesContainer.appendChild(cubsPage);

// --- TIMER LOGIC ---
let timerSeconds = 0;
let timerInterval = null;

function updateTimerDisplay() {
    const minutes = Math.floor(timerSeconds / 60);
    const seconds = timerSeconds % 60;
    timerDisplay.textContent = `${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;
}

timerStartBtn.addEventListener("click", () =&amp;gt; {
    if (timerInterval === null) {
        timerInterval = setInterval(() =&amp;gt; {
            timerSeconds++;
            updateTimerDisplay();
        }, 1000);
    }
});

timerStopBtn.addEventListener("click", () =&amp;gt; {
    if (timerInterval !== null) {
        clearInterval(timerInterval);
        timerInterval = null;
    }
});

timerResetBtn.addEventListener("click", () =&amp;gt; {
    clearInterval(timerInterval);
    timerInterval = null;
    timerSeconds = 0;
    updateTimerDisplay();
});

updateTimerDisplay();

// --- TYPING TEST LOGIC ---
const wordsForTypingTest = words.slice(0, 50);
let level = 0;
let score = 0;
let timeLeft = 30;
let typingTimer = null;
let currentTypingWordIndex = 0;

function startTypingTest() {
    level = 1;
    score = 0;
    timeLeft = 30;
    currentTypingWordIndex = 0;
    typedWordsDiv.innerHTML = "";
    typingInput.value = "";
    typingInput.disabled = false;
    typingInput.focus();
    updateTypingInfo();
    showTypingWord(wordsForTypingTest[currentTypingWordIndex]);

    if (typingTimer) clearInterval(typingTimer);
    typingTimer = setInterval(() =&amp;gt; {
        timeLeft--;
        updateTypingInfo();
        if (timeLeft &amp;lt;= 0) {
            clearInterval(typingTimer);
            typingTimer = null;
            console.log(`TIME UP! YOUR SCORE: ${score}`);
        }
    }, 1000);
}

function updateTypingInfo() {
    document.getElementById("level").textContent = level;
    document.getElementById("score").textContent = score;
    document.getElementById("time").textContent = timeLeft;
}

function showTypingWord(word) {
    typingWord.textContent = word;
}

typingInput.addEventListener("input", () =&amp;gt; {
    if (typingInput.value.toUpperCase() === typingWord.textContent) {
        score += 10;
        level++;
        currentTypingWordIndex = (currentTypingWordIndex + 1) % wordsForTypingTest.length;
        typedWordsDiv.textContent += typingWord.textContent + " ";
        typingInput.value = "";
        showTypingWord(wordsForTypingTest[currentTypingWordIndex]);
        updateTypingInfo();
    }
});

startBtnTyping.addEventListener("click", () =&amp;gt; {
    if (typingTimer === null) {
        startTypingTest();
    }
});

// --- PIXEL CREATOR LOGIC ---
const colorPicker = pixelCreatorToolbar.querySelector('#colorPicker');
const clearCanvasBtn = pixelCreatorToolbar.querySelector('#clearCanvasBtn');
const saveImageBtn = pixelCreatorToolbar.querySelector('#saveImageBtn');
const playerNameInput = pixelCreatorToolbar.querySelector('#playerName');

if (playerNameInput) {
    playerNameInput.addEventListener('input', function() {
        this.value = this.value.toUpperCase();
    });
}

const pixelCtx = pixelCanvas.getContext('2d');
const gridSize = 32;
const cellSize = pixelCanvas.width / gridSize;
colorPicker.value = "#000000";

function drawGrid() {
    pixelCtx.clearRect(0, 0, pixelCanvas.width, pixelCanvas.height);
    pixelCtx.strokeStyle = "#ccc";
    for (let i = 0; i &amp;lt;= gridSize; i++) {
        pixelCtx.beginPath();
        pixelCtx.moveTo(i * cellSize, 0);
        pixelCtx.lineTo(i * cellSize, pixelCanvas.height);
        pixelCtx.stroke();

        pixelCtx.beginPath();
        pixelCtx.moveTo(0, i * cellSize);
        pixelCtx.lineTo(pixelCanvas.width, i * cellSize);
        pixelCtx.stroke();
    }
}

function fillCell(x, y, color) {
    pixelCtx.fillStyle = color;
    pixelCtx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
}

function getCellFromEvent(e) {
    const rect = pixelCanvas.getBoundingClientRect();
    const x = Math.floor((e.clientX - rect.left) / cellSize);
    const y = Math.floor((e.clientY - rect.top) / cellSize);
    return { x, y };
}

let isDrawing = false;
pixelCanvas.addEventListener("mousedown", e =&amp;gt; {
    isDrawing = true;
    const { x, y } = getCellFromEvent(e);
    fillCell(x, y, colorPicker.value);
});

pixelCanvas.addEventListener("mousemove", e =&amp;gt; {
    if (isDrawing) {
        const { x, y } = getCellFromEvent(e);
        fillCell(x, y, colorPicker.value);
    }
});

pixelCanvas.addEventListener("mouseup", () =&amp;gt; {
    isDrawing = false;
});

pixelCanvas.addEventListener("mouseleave", () =&amp;gt; {
    isDrawing = false;
});

clearCanvasBtn.addEventListener("click", clearPixelCanvas);
function clearPixelCanvas() {
    pixelCtx.clearRect(0, 0, pixelCanvas.width, pixelCanvas.height);
    drawGrid();
}

saveImageBtn.addEventListener("click", saveImage);
function saveImage() {
    const name = playerNameInput.value || "pixel_art";
    const link = document.createElement('a');
    link.download = name + ".png";
    link.href = pixelCanvas.toDataURL();
    link.click();
}

// --- MATH GAME LOGIC ---
let currentMathQuestion = {};
let mathGameScore = 0;
let selectedOperation = null;

function generateMathQuestion() {
    let num1, num2, answer;
    let questionString;

    const maxNum = 12;
    const minNum = 1;

    if (selectedOperation === '+') {
        num1 = Math.floor(Math.random() * maxNum) + minNum;
        num2 = Math.floor(Math.random() * maxNum) + minNum;
        answer = num1 + num2;
        questionString = `${num1} + ${num2} = ?`;
    } else if (selectedOperation === '-') {
        num1 = Math.floor(Math.random() * maxNum) + minNum;
        num2 = Math.floor(Math.random() * num1) + minNum;
        answer = num1 - num2;
        questionString = `${num1} - ${num2} = ?`;
    } else if (selectedOperation === '*') {
        num1 = Math.floor(Math.random() * 10) + minNum;
        num2 = Math.floor(Math.random() * 10) + minNum;
        answer = num1 * num2;
        questionString = `${num1} X ${num2} = ?`;
    } else if (selectedOperation === '/') {
        let divisor;
        let dividend;
        do {
            divisor = Math.floor(Math.random() * 9) + 2;
            answer = Math.floor(Math.random() * 9) + 1;
            dividend = divisor * answer;
        } while (dividend &amp;gt; 100 || dividend % divisor !== 0);
        num1 = dividend;
        num2 = divisor;
        questionString = `${num1} ÷ ${num2} = ?`;
    } else {
        mathQuestion.textContent = "SELECT AN OPERATION TO START!";
        return;
    }

    currentMathQuestion = { question: questionString, answer: answer };
    mathQuestion.textContent = questionString;
    mathAnswerInput.value = "";
    mathFeedback.textContent = "";
    mathAnswerInput.disabled = false;
    mathAnswerInput.focus();
}

function updateMathScore() {
    mathScore.textContent = `SCORE: ${mathGameScore}`;
}

document.querySelectorAll('.math-operation-btn').forEach(button =&amp;gt; {
    button.addEventListener('click', (event) =&amp;gt; {
        selectedOperation = event.target.dataset.operation;
        mathGameScore = 0;
        updateMathScore();
        generateMathQuestion();
    });
});

mathAnswerInput.addEventListener('keydown', (e) =&amp;gt; {
    if (e.key === 'Enter' &amp;amp;&amp;amp; selectedOperation) {
        const userAnswer = parseInt(mathAnswerInput.value);
        if (!isNaN(userAnswer)) {
            if (userAnswer === currentMathQuestion.answer) {
                mathFeedback.textContent = "CORRECT!";
                mathFeedback.style.color = "#0f0";
                mathFeedback.style.textShadow = "0 0 10px #0f0";
                mathGameScore += 10;
            } else {
                mathFeedback.textContent = `INCORRECT. THE ANSWER WAS ${currentMathQuestion.answer}.`;
                mathFeedback.style.color = "#f00";
                mathFeedback.style.textShadow = "0 0 10px #f00";
                mathGameScore = Math.max(0, mathGameScore - 5);
            }
            updateMathScore();
            setTimeout(generateMathQuestion, 1000);
        } else {
            mathFeedback.textContent = "PLEASE ENTER A NUMBER.";
            mathFeedback.style.color = "#ff0";
            mathFeedback.style.textShadow = "0 0 10px #ff0";
        }
    }
});

// --- VOICE CREATOR LOGIC ---
let voiceHistory = [""]; // Stores states of the textarea for undo/redo
let voiceHistoryIndex = 0;
let voicesLoaded = false; // Flag to track if voices are loaded

// Function to load voices and set a default
function loadVoices() {
    if (voicesLoaded) return; // Only load once

    const availableVoices = speechSynthesis.getVoices();
    if (availableVoices.length === 0) {
        voiceFeedback.textContent = "LOADING VOICES...";
        // If voices aren't immediately available, wait for them to load
        speechSynthesis.onvoiceschanged = () =&amp;gt; {
            const updatedVoices = speechSynthesis.getVoices();
            if (updatedVoices.length &amp;gt; 0) {
                voicesLoaded = true;
                voiceFeedback.textContent = "VOICES LOADED. READY TO SPEAK.";
                voiceFeedback.style.color = "#0f0";
                console.log("SpeechSynthesis voices loaded.");
            } else {
                voiceFeedback.textContent = "NO VOICES FOUND. TRY A DIFFERENT BROWSER.";
                voiceFeedback.style.color = "#f00";
                console.error("No SpeechSynthesis voices found after loading.");
            }
            speechSynthesis.onvoiceschanged = null; // Remove listener after voices are loaded
        };
        // If onvoiceschanged doesn't fire, it might be a browser issue or no voices
        setTimeout(() =&amp;gt; {
            if (!voicesLoaded &amp;amp;&amp;amp; speechSynthesis.getVoices().length === 0) {
                voiceFeedback.textContent = "NO VOICES FOUND. TRY A DIFFERENT BROWSER OR CHECK SETTINGS.";
                voiceFeedback.style.color = "#f00";
                console.error("SpeechSynthesis voices not loaded after timeout.");
            }
        }, 3000); // Give it some time to load
    } else {
        voicesLoaded = true;
        voiceFeedback.textContent = "READY TO SPEAK.";
        voiceFeedback.style.color = "#0f0";
        console.log("SpeechSynthesis voices already loaded.");
    }
}

function updateVoiceHistory() {
    const currentText = voiceInput.value;
    if (currentText !== voiceHistory[voiceHistoryIndex]) {
        // If user typed something new, clear future history and add current state
        voiceHistory = voiceHistory.slice(0, voiceHistoryIndex + 1);
        voiceHistory.push(currentText);
        voiceHistoryIndex = voiceHistory.length - 1;
    }
}

voiceInput.addEventListener('input', updateVoiceHistory);

playVoiceBtn.addEventListener('click', () =&amp;gt; {
    const textToSpeak = voiceInput.value.trim();
    if (!('speechSynthesis' in window)) {
        voiceFeedback.textContent = "SPEECH SYNTHESIS NOT SUPPORTED BY YOUR BROWSER.";
        voiceFeedback.style.color = "#f00"; // Red for error
        console.error("SpeechSynthesis API not supported in this browser.");
        return;
    }

    if (!voicesLoaded) {
        voiceFeedback.textContent = "VOICES ARE STILL LOADING OR UNAVAILABLE. PLEASE WAIT.";
        voiceFeedback.style.color = "#ff0"; // Yellow for warning
        loadVoices(); // Try to load them again if not loaded
        return;
    }

    if (textToSpeak) {
        speechSynthesis.cancel(); // Stop any ongoing speech
        const utterance = new SpeechSynthesisUtterance(textToSpeak);

        // Try to find a suitable voice, e.g., a US English voice
        const voices = speechSynthesis.getVoices();
        const selectedVoice = voices.find(voice =&amp;gt; voice.lang === 'en-US' &amp;amp;&amp;amp; voice.name.includes('Google')) || voices.find(voice =&amp;gt; voice.lang.startsWith('en')) || voices[0];
        if (selectedVoice) {
            utterance.voice = selectedVoice;
        } else {
            voiceFeedback.textContent = "NO ENGLISH VOICES FOUND. USING DEFAULT.";
            voiceFeedback.style.color = "#ff0";
            console.warn("No specific English voice found, using browser default.");
        }

        utterance.onstart = () =&amp;gt; {
            voiceFeedback.textContent = "PLAYING VOICE...";
            voiceFeedback.style.color = "#0f0"; // Green for playing
        };
        utterance.onend = () =&amp;gt; {
            voiceFeedback.textContent = "VOICE PLAYBACK COMPLETE.";
            voiceFeedback.style.color = "#0ff"; // Cyan for complete
        };
        utterance.onerror = (event) =&amp;gt; {
            voiceFeedback.textContent = `VOICE PLAYBACK ERROR: ${event.error}. TRY AGAIN.`;
            voiceFeedback.style.color = "#f00"; // Red for error
            console.error("SpeechSynthesisUtterance error:", event);
        };
        speechSynthesis.speak(utterance);
    } else {
        voiceFeedback.textContent = "PLEASE TYPE SOME WORDS TO SPEAK.";
        voiceFeedback.style.color = "#ff0"; // Yellow for warning
    }
});

clearVoiceBtn.addEventListener('click', () =&amp;gt; {
    speechSynthesis.cancel(); // Stop any ongoing speech
    voiceInput.value = "";
    updateVoiceHistory(); // Save the cleared state
    voiceFeedback.textContent = ""; // Clear feedback
});

undoVoiceBtn.addEventListener('click', () =&amp;gt; {
    speechSynthesis.cancel(); // Stop any ongoing speech
    if (voiceHistoryIndex &amp;gt; 0) {
        voiceHistoryIndex--;
        voiceInput.value = voiceHistory[voiceHistoryIndex];
        voiceFeedback.textContent = "UNDO COMPLETE.";
        voiceFeedback.style.color = "#0ff";
    } else {
        voiceFeedback.textContent = "NOTHING TO UNDO.";
        voiceFeedback.style.color = "#ff0";
    }
});

redoVoiceBtn.addEventListener('click', () =&amp;gt; {
    speechSynthesis.cancel(); // Stop any ongoing speech
    if (voiceHistoryIndex &amp;lt; voiceHistory.length - 1) {
        voiceHistoryIndex++;
        voiceInput.value = voiceHistory[voiceHistoryIndex];
        voiceFeedback.textContent = "REDO COMPLETE.";
        voiceFeedback.style.color = "#0ff";
    } else {
        voiceFeedback.textContent = "NOTHING TO REDO.";
        voiceFeedback.style.color = "#ff0";
    }
});

saveTextBtn.addEventListener('click', () =&amp;gt; {
    speechSynthesis.cancel(); // Stop any ongoing speech
    const textToSave = voiceInput.value.trim();
    if (textToSave) {
        const blob = new Blob([textToSave], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'proclub_voice_text.txt';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
        voiceFeedback.textContent = "TEXT SAVED AS 'PROCLUB_VOICE_TEXT.TXT'.";
        voiceFeedback.style.color = "#0f0";
    } else {
        voiceFeedback.textContent = "NOTHING TO SAVE.";
        voiceFeedback.style.color = "#ff0";
    }
});

// --- CUBS 3D LOGIC (Three.js) ---
let scene, camera, renderer;
let cub, mixer; // Declare cub and mixer globally for animation
let clock = new THREE.Clock(); // For animation updates
let rotationSpeed = 0.005; // Initial rotation speed for the camera
let rotating = false; // Flag to control camera auto-rotation
// Removed jumpAction as the JUMP button is removed
// let jumpAction; // Declare jumpAction globally

// Function to initialize the 3D scene for cubs
function initCubs3D() {
    // Only initialize if not already initialized
    if (scene) return;

    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x333333); // Dark background for the scene itself

    camera = new THREE.PerspectiveCamera(75, cubsCanvas.clientWidth / cubsCanvas.clientHeight, 0.1, 1000);
    camera.position.set(0, 1.5, 3); // Position the camera

    renderer = new THREE.WebGLRenderer({ canvas: cubsCanvas, antialias: true });
    renderer.setSize(cubsCanvas.clientWidth, cubsCanvas.clientHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.shadowMap.enabled = true; // Enable shadows

    // Lights
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); // Soft white light
    scene.add(ambientLight);

    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(5, 10, 7.5);
    directionalLight.castShadow = true;
    directionalLight.shadow.mapSize.width = 1024;
    directionalLight.shadow.mapSize.height = 1024;
    directionalLight.shadow.camera.near = 0.5;
    directionalLight.shadow.camera.far = 50;
    directionalLight.shadow.camera.left = -10;
    directionalLight.shadow.camera.right = 10;
    directionalLight.shadow.camera.top = 10;
    directionalLight.shadow.camera.bottom = -10;
    scene.add(directionalLight);

    // Ground plane
    const planeGeometry = new THREE.PlaneGeometry(10, 10);
    const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x888888, side: THREE.DoubleSide });
    const plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.rotation.x = Math.PI / 2;
    plane.receiveShadow = true;
    plane.position.y = -0.5;
    scene.add(plane);

    // Create a simple cub (or multiple cubs if you like!)
    const createCub = (color, x, y, z) =&amp;gt; {
        const cubGeometry = new THREE.BoxGeometry(1, 1, 1);
        const cubMaterial = new THREE.MeshStandardMaterial({ color: color });
        const cubMesh = new THREE.Mesh(cubGeometry, cubMaterial);
        cubMesh.position.set(x, y, z);
        cubMesh.castShadow = true;
        cubMesh.receiveShadow = false; // Cub doesn't receive its own shadow, but casts one
        return cubMesh;
    };

    cub = createCub(cubsColorPicker.value, 0, 0, 0); // Main cub
    scene.add(cub);

    // Removed animation logic as the JUMP button is removed
    // Add animation (simple up and down for now)
    // mixer = new THREE.AnimationMixer(cub);
    // const times = [0, 0.5, 1]; // Keyframe times
    // const values = [0, 0.5, 0]; // Y positions
    // const positionKF = new THREE.VectorKeyframeTrack('position', times, values); // Animate Y position of the cub
    // const clip = new THREE.AnimationClip('Jump', 1, [positionKF]);
    // jumpAction = mixer.clipAction(clip); // Assign to global jumpAction
    // jumpAction.loop = THREE.LoopOnce; // Play once per jump click
    // jumpAction.clampWhenFinished = true; // Stay at end position until reset

    // Basic orbit controls (optional, for debugging or manual control)
    // const controls = new THREE.OrbitControls(camera, renderer.domElement);
    // controls.enableDamping = true;
    // controls.dampingFactor = 0.25;

    // Start auto-rotation by default
    rotating = true;

    // Animation loop
    const animate = () =&amp;gt; {
        requestAnimationFrame(animate);
        const delta = clock.getDelta();
        if (mixer) mixer.update(delta); // mixer might still be used for other animations if added later

        if (rotating) {
            camera.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), rotationSpeed);
            camera.lookAt(0, 0, 0); // Keep looking at the center
        }

        renderer.render(scene, camera);
    };
    animate();
}

// Handle window resize for cubs canvas
function onWindowResizeCubs() {
    if (camera &amp;amp;&amp;amp; renderer &amp;amp;&amp;amp; cubsCanvas) {
        const width = cubsCanvas.clientWidth;
        const height = cubsCanvas.clientHeight;
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        renderer.setSize(width, height);
    }
}
window.addEventListener('resize', onWindowResizeCubs);

// CUBS Control Event Listeners
cubsColorPicker.addEventListener('input', (event) =&amp;gt; {
    if (cub) {
        cub.material.color.set(event.target.value);
    }
});

// Removed the JUMP! button's event listener as the button is removed
// cubsJumpBtn.addEventListener('click', () =&amp;gt; {
//     if (jumpAction) { // Use the global jumpAction
//         jumpAction.stop(); // Stop previous animation if any
//         jumpAction.play(); // Play the jump animation
//     }
// });

cubsRotateLeftBtn.addEventListener('click', () =&amp;gt; {
    rotating = false; // Stop auto-rotation
    camera.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 10); // Rotate by 18 degrees
    camera.lookAt(0, 0, 0);
    renderer.render(scene, camera); // Render once
});

cubsRotateRightBtn.addEventListener('click', () =&amp;gt; {
    rotating = false; // Stop auto-rotation
    camera.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI / 10); // Rotate by -18 degrees
    camera.lookAt(0, 0, 0);
    renderer.render(scene, camera); // Render once
});

// --- NAVIGATION LOGIC ---
// IMPORTANT: Ensure all page variables are defined before being used in this array
const pages = [page0, page1, page2, page3, page4, page5, page6, voiceCreatorPage, cubsPage];
let currentPageIndex = 0; // Starts at the home page

const navLabels = ["GO TO WORD CYCLE", "GO TO FEELINGS", "GO TO TYPING TEST", "GO TO TIMER", "GO TO PIXEL CREATOR", "GO TO BRAIN BLASTER", "GO TO VOICE CREATOR", "GO TO CUBS", "GO TO HOME"];

navBtn.addEventListener('click', () =&amp;gt; {
    // Hide the current page
    pages[currentPageIndex].style.display = 'none';

    // IF THE CURRENT PAGE IS THE HOME PAGE, HIDE IT PERMANENTLY.
    if (currentPageIndex === 0) {
        page0.style.display = 'none'; // Ensure it's hidden once clicked away
    }

    // Move to the next page
    currentPageIndex = (currentPageIndex + 1) % pages.length;

    // Show the new current page
    pages[currentPageIndex].style.display = 'flex'; // Use 'flex' for page display

    // Update button text
    navBtn.textContent = navLabels[currentPageIndex];

    // Specific initialization for pages that need it when shown
    if (pages[currentPageIndex] === page5) { // Pixel Creator
        drawGrid(); // Redraw grid for pixel creator when it becomes visible
    } else if (pages[currentPageIndex] === cubsPage) { // CUBS Page
        initCubs3D(); // Initialize Three.js scene for cubs
        onWindowResizeCubs(); // Ensure canvas is correctly sized on first display
    } else if (pages[currentPageIndex] === voiceCreatorPage) {
        loadVoices(); // Ensure voices are loaded for voice creator
    }
});

// Initialize music and voices on window load
window.onload = function() {
    // Attempt to play the background music.
    const music = document.getElementById('backgroundMusic');
    if (music) {
        music.play().catch(e =&amp;gt; console.log("Autoplay prevented:", e.message));
    }

    // Load voices for SpeechSynthesis (called here and when page is navigated to)
    loadVoices();
};
Enter fullscreen mode Exit fullscreen mode

&lt;/script&gt;
</code></pre></div>
<p></body><br>
</html></p>

Top comments (0)