Build a Study Quiz App with AI Hints

Build a browser quiz app with AI-powered hints using the Gemini API.

Introduction

30 Second Summary

Studying for exams means reviewing the same material over and over. Having a tool that quizzes you and gives you a nudge when you're stuck makes that process much more effective.

In this project, you will build a browser-based study quiz app that lets you create your own questions, take quizzes, and get AI-powered hints from Google Gemini when you need help.

What You'll Build

A web application using HTML, CSS, and JavaScript that stores questions locally and connects to the Gemini API for intelligent study hints.

By the end of this project, you'll have:

  • A question creator with support for multiple-choice and identification question types.
  • A quiz interface that presents your questions one at a time with instant feedback and scoring.
  • An AI hint system powered by Google Gemini that gives helpful clues without revealing the answer.
  • Secret Mission: A score history tracker that records your quiz results over time so you can see your improvement.

Are there any prerequisites?

You should have a basic understanding of HTML and CSS. No prior JavaScript experience is required. You will also need a Google account to get a free Gemini API key, which is covered in Step 1.

Set Up Your Project

Before you start building the quiz app, you need two things: access to the AI that will power your hints, and a place to write your code.

In this step, you will get a free API key from Google and create the project files in VS Code.

In this step, get ready to:
  • Get your free Gemini API key from Google AI Studio.
  • Create a project folder with three files in VS Code.
  • Verify everything works by opening your page in Google Chrome.
Get your Gemini API key

The Gemini API is what gives your app the ability to generate AI-powered hints. To use it, you need an API key that identifies your app to Google's servers.

  • Open your browser and go to Google AI Studio.
  • Sign in with your Google account.
  • Click Get API Key in the left sidebar.
  • Click Create API key.
  • Copy the key that appears and save it somewhere safe (a text file or notes app works).

Why save the key separately?

You will paste this key into your JavaScript code in a later step. Some providers only show the key once, so keeping a copy prevents you from having to regenerate it.

No credit card needed

The Gemini API free tier gives you 1,500 requests per day at no cost. You will not be asked for billing information.

Create the project files

Your quiz app needs three files: one for structure (HTML), one for styling (CSS), and one for logic (JavaScript). You will create all three inside a single project folder.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Open the integrated terminal by clicking Terminal in the top menu bar, then clicking New Terminal.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir quiz-app
cd quiz-app
  • Create the three project files by running this command:

macOS

touch index.html style.css script.js

Windows

New-Item index.html, style.css, script.js
  • Confirm the files exist by running:
ls

You should see index.html, style.css, and script.js listed in the output.

  • Now open this folder in VS Code by clicking File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the quiz-app folder, and click Open.

You should now see all three files in the Explorer sidebar on the left.

Don't see the Explorer sidebar?

Click View in the top menu bar, then click Explorer to toggle it on. You can also press Cmd+Shift+E (macOS) or Ctrl+Shift+E (Windows).

Help me find my project files in VS Code

Verify your setup in the browser

Opening your HTML file in a browser confirms that everything is connected properly before you start writing code.

  • In VS Code's Explorer sidebar, right-click index.html and click Copy Path.
  • Open Google Chrome.
  • Click the address bar at the top and paste the file path you just copied.
  • Press Enter. You should see a blank white page.
  • Open the browser developer tools by pressing Cmd+Option+I (macOS) or F12 (Windows).
  • Click the Console tab.
  • Confirm there are no red error messages in the console.

Why check the console?

The browser console shows JavaScript errors and warnings. Starting with a clean console means any errors you see later come from code you wrote, making them much easier to debug.

Seeing a 'file not found' error?

Make sure the path in your address bar points to the index.html file inside the quiz-app folder on your Desktop. The path should end with quiz-app/index.html.

Help me open my HTML file in Chrome

Your project is ready to go. In the next step, you will build the question creator form and bring the page to life with HTML, CSS, and JavaScript.

Build the Question Creator

Your quiz app needs a way for you to add your own study questions. Without questions, there is nothing to quiz yourself on.

In this step, you will build the HTML form for creating questions, write the JavaScript logic to save them, and style everything so it looks clean. By the end, you will have a working question creator that persists your questions between page refreshes using localStorage.

In this step, get ready to:
  • Build the HTML page layout with a question form and question bank display.
  • Write JavaScript to handle form submissions and save questions to localStorage.
  • Style the interface with CSS for a clean, usable design.
Create the page layout

The app needs two main areas: a form for creating questions and a section where quizzes will happen later. The form includes a dropdown to pick the question type, inputs for the question text and answer options, and a save button.

  • Switch to index.html in VS Code and paste the following HTML:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Study Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Study Quiz App</h1>
        <nav class="tabs">
            <button class="tab-btn active" id="tab-create">Create Questions</button>
            <button class="tab-btn" id="tab-quiz">Take Quiz</button>
        </nav>

        <!-- Create Questions Section -->
        <section id="create-section" class="section active">
            <h2>Add a Question</h2>
            <form id="question-form">
                <div class="form-group">
                    <label for="question-type">Question Type:</label>
                    <select id="question-type">
                        <option value="multiple-choice">Multiple Choice</option>
                        <option value="identification">Identification</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="question-text">Question:</label>
                    <input type="text" id="question-text" placeholder="Enter your question" required>
                </div>
                <div id="options-container">
                    <div class="form-group">
                        <label>Options:</label>
                        <input type="text" class="option-input" placeholder="Option A" required>
                        <input type="text" class="option-input" placeholder="Option B" required>
                        <input type="text" class="option-input" placeholder="Option C" required>
                        <input type="text" class="option-input" placeholder="Option D" required>
                    </div>
                    <div class="form-group">
                        <label for="correct-option">Correct Option (A, B, C, or D):</label>
                        <input type="text" id="correct-option" placeholder="e.g., A" required>
                    </div>
                </div>
                <div id="identification-container" class="hidden">
                    <div class="form-group">
                        <label for="correct-answer">Correct Answer:</label>
                        <input type="text" id="correct-answer" placeholder="Enter the correct answer">
                    </div>
                </div>
                <button type="submit" class="btn btn-primary">Save Question</button>
            </form>

            <h2>Question Bank (<span id="question-count">0</span>)</h2>
            <div id="question-list"></div>
        </section>

        <!-- Quiz Section -->
        <section id="quiz-section" class="section hidden">
            <div id="quiz-start">
                <p>You have <span id="quiz-question-count">0</span> questions in your bank.</p>
                <button class="btn btn-primary" id="start-quiz-btn">Start Quiz</button>
            </div>
            <div id="quiz-active" class="hidden"></div>
            <div id="quiz-results" class="hidden"></div>
        </section>
    </div>
    <script src="script.js"></script>
</body>
</html>

What does this HTML do?

The page is split into two sections controlled by tab buttons. The create-section holds the question form with a dropdown for question type, text inputs for the question and answers, and a save button. The quiz-section is a placeholder stub you will build out in the next step.

The hidden class controls which section is visible. The identification answer input starts hidden because multiple-choice is the default question type.

  • Save index.html.
  • Refresh your browser. You should see the heading "Study Quiz App", two tab buttons, and a form with inputs and a blue "Save Question" button. It will look unstyled for now.

Seeing a blank page still?

Make sure you saved the file. Check that your browser tab is pointing at the index.html file inside your quiz-app folder. If you see raw HTML text instead of a rendered page, confirm the file extension is .html and not .txt.

Help me troubleshoot my HTML page

Add the question creation logic

The form needs JavaScript to capture input, build question objects, store them in the browser, and render the saved questions on the page. You will use localStorage so that questions persist even after a page refresh.

  • Switch to script.js in VS Code and paste the following code to set up configuration and state:
// ===== Configuration =====
const API_KEY = "your-api-key-here";
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;

// ===== State =====
let questions = JSON.parse(localStorage.getItem("quizQuestions")) || [];
let currentQuizIndex = 0;
let score = 0;
let quizQuestions = [];

What does this code do?

The API_KEY constant is where you will paste your Gemini API key later. The API_URL builds the full endpoint URL using that key.

The questions array loads any previously saved questions from localStorage. If nothing is stored yet, it defaults to an empty array. The other variables will be used during quiz mode in a later step.

  • Below the state section, paste the DOM element references and tab navigation listeners:
// ===== DOM Elements =====
const tabCreate = document.getElementById("tab-create");
const tabQuiz = document.getElementById("tab-quiz");
const createSection = document.getElementById("create-section");
const quizSection = document.getElementById("quiz-section");
const questionForm = document.getElementById("question-form");
const questionType = document.getElementById("question-type");
const optionsContainer = document.getElementById("options-container");
const identificationContainer = document.getElementById("identification-container");
const questionList = document.getElementById("question-list");
const questionCount = document.getElementById("question-count");
const quizQuestionCount = document.getElementById("quiz-question-count");
const startQuizBtn = document.getElementById("start-quiz-btn");
const quizStart = document.getElementById("quiz-start");
const quizActive = document.getElementById("quiz-active");
const quizResults = document.getElementById("quiz-results");

// ===== Tab Navigation =====
tabCreate.addEventListener("click", function () {
    tabCreate.classList.add("active");
    tabQuiz.classList.remove("active");
    createSection.classList.remove("hidden");
    createSection.classList.add("active");
    quizSection.classList.add("hidden");
    quizSection.classList.remove("active");
});

tabQuiz.addEventListener("click", function () {
    tabQuiz.classList.add("active");
    tabCreate.classList.remove("active");
    quizSection.classList.remove("hidden");
    quizSection.classList.add("active");
    createSection.classList.add("hidden");
    createSection.classList.remove("active");
    quizQuestionCount.textContent = questions.length;
});

// ===== Question Type Toggle =====
questionType.addEventListener("change", function () {
    if (this.value === "multiple-choice") {
        optionsContainer.classList.remove("hidden");
        identificationContainer.classList.add("hidden");
    } else {
        optionsContainer.classList.add("hidden");
        identificationContainer.classList.remove("hidden");
    }
});

What does this code do?

The DOM element references grab every HTML element your JavaScript will interact with. Caching them in variables avoids repeated getElementById calls.

The tab navigation listeners swap which section is visible when you click "Create Questions" or "Take Quiz". The question type toggle shows or hides the multiple-choice options versus the single-answer input depending on the dropdown selection.

  • Below the toggle listener, paste the form submission handler:
// ===== Save Question =====
questionForm.addEventListener("submit", function (e) {
    e.preventDefault();

    const type = questionType.value;
    const text = document.getElementById("question-text").value.trim();

    let question = {
        id: Date.now(),
        type: type,
        question: text,
    };

    if (type === "multiple-choice") {
        const optionInputs = document.querySelectorAll(".option-input");
        const options = Array.from(optionInputs).map(function (input) {
            return input.value.trim();
        });
        const correctLetter = document.getElementById("correct-option").value.trim().toUpperCase();
        const letterIndex = correctLetter.charCodeAt(0) - 65;

        if (letterIndex < 0 || letterIndex > 3) {
            alert("Please enter A, B, C, or D for the correct option.");
            return;
        }

        question.options = options;
        question.answer = options[letterIndex];
    } else {
        const answer = document.getElementById("correct-answer").value.trim();
        if (!answer) {
            alert("Please enter the correct answer.");
            return;
        }
        question.answer = answer;
    }

    questions.push(question);
    saveQuestions();
    renderQuestionList();
    questionForm.reset();
    questionType.dispatchEvent(new Event("change"));
});

What does this code do?

When the form is submitted, e.preventDefault() stops the page from reloading. The handler reads the question type and text, then builds a question object.

For multiple-choice questions, it collects all four option values and converts the correct letter (A, B, C, or D) into an array index using charCodeAt. For identification questions, it simply grabs the text answer. After saving, it resets the form and re-triggers the type change event to restore the default input state.

  • Below the form handler, paste the utility functions and initializer:
// ===== Save to localStorage =====
function saveQuestions() {
    localStorage.setItem("quizQuestions", JSON.stringify(questions));
}

// ===== Render Question List =====
function renderQuestionList() {
    questionCount.textContent = questions.length;
    quizQuestionCount.textContent = questions.length;

    if (questions.length === 0) {
        questionList.innerHTML = "<p>No questions yet. Add some above!</p>";
        return;
    }

    questionList.innerHTML = questions
        .map(function (q) {
            return `
                <div class="question-card">
                    <div class="question-card-header">
                        <span class="question-type-badge">${q.type}</span>
                        <button class="btn btn-delete" onclick="deleteQuestion(${q.id})">Delete</button>
                    </div>
                    <p><strong>${q.question}</strong></p>
                    <p>Answer: ${q.answer}</p>
                </div>
            `;
        })
        .join("");
}

// ===== Delete Question =====
function deleteQuestion(id) {
    questions = questions.filter(function (q) {
        return q.id !== id;
    });
    saveQuestions();
    renderQuestionList();
}

// ===== Initialize =====
renderQuestionList();

What does this code do?

The saveQuestions() function converts the questions array to a JSON string and stores it in localStorage under the key quizQuestions. This is what makes your data survive page refreshes.

The renderQuestionList() function updates the question count and builds HTML cards for each saved question. The deleteQuestion() function filters out a question by its ID and re-renders the list. The final renderQuestionList() call at the bottom runs on page load to display any previously saved questions.

  • Save script.js.
  • Refresh your browser. The page should still display the form. Open the Console tab in DevTools (press F12) and confirm there are no errors.

Seeing an error in the console?

If you see a message about null or "cannot read properties of null", make sure the <script src="script.js"></script> tag is at the bottom of your HTML body (just before </body>). The script must load after the HTML elements exist.

Help me fix my JavaScript console error

Style the interface

The HTML is functional but needs styling to be usable. You will add CSS for layout, form elements, buttons, and the question cards.

  • Switch to style.css in VS Code and paste the following styles:
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
    line-height: 1.6;
}

.container {
    max-width: 700px;
    margin: 40px auto;
    padding: 20px;
}

h1 {
    text-align: center;
    color: #1a73e8;
    margin-bottom: 20px;
    font-size: 2rem;
}

h2 {
    margin: 20px 0 10px;
    color: #444;
}

.tabs {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.tab-btn {
    flex: 1;
    padding: 12px;
    border: none;
    background-color: #e0e0e0;
    color: #555;
    font-size: 1rem;
    cursor: pointer;
    border-radius: 8px;
    transition: background-color 0.2s;
}

.tab-btn.active {
    background-color: #1a73e8;
    color: white;
}

.tab-btn:hover {
    background-color: #c0c0c0;
}

.tab-btn.active:hover {
    background-color: #1557b0;
}

.section {
    background-color: white;
    padding: 24px;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.section.hidden {
    display: none;
}

.hidden {
    display: none;
}

.form-group {
    margin-bottom: 16px;
}

.form-group label {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    color: #555;
}

.form-group input,
.form-group select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 1rem;
    margin-bottom: 8px;
}

.form-group input:focus,
.form-group select:focus {
    outline: none;
    border-color: #1a73e8;
    box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
}

.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.2s;
}

.btn-primary {
    background-color: #1a73e8;
    color: white;
}

.btn-primary:hover {
    background-color: #1557b0;
}

.btn-delete {
    background-color: #ea4335;
    color: white;
    padding: 6px 12px;
    font-size: 0.85rem;
}

.btn-delete:hover {
    background-color: #c5221f;
}

.question-card {
    background-color: #f8f9fa;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 12px;
}

.question-card .question-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}

.question-card .question-type-badge {
    font-size: 0.75rem;
    padding: 2px 8px;
    border-radius: 4px;
    background-color: #e8f0fe;
    color: #1a73e8;
    font-weight: 600;
}

What do these styles do?

The CSS centers the app in a 700px container, styles the tab buttons to highlight the active one in blue, and gives the form inputs rounded borders with a focus glow. The .hidden class uses display: none to control section visibility.

Question cards get a light background with a subtle border. The type badge shows "multiple-choice" or "identification" in a small blue label. The delete button is styled in red to signal a destructive action.

  • Save style.css and refresh your browser.
  • You should see a centered white card with a blue heading, styled tab buttons, and a clean form with rounded inputs. Try filling in a question and clicking Save Question. A question card should appear in the "Question Bank" area below the form.
  • Refresh the page. Your saved question should still be there.

Question disappears after refresh?

Open DevTools (F12), go to the **Application** tab, and expand **Local Storage** on the left. You should see a quizQuestions key with your question data stored as JSON. If it is missing, make sure you pasted the saveQuestions() function and that it is called inside the form submit handler.

Help me debug why my questions are not saving to localStorage

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files before moving on.

ⓧ I'd like to double check the full code

Here is the complete code for each file at this point.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Study Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Study Quiz App</h1>
        <nav class="tabs">
            <button class="tab-btn active" id="tab-create">Create Questions</button>
            <button class="tab-btn" id="tab-quiz">Take Quiz</button>
        </nav>

        <!-- Create Questions Section -->
        <section id="create-section" class="section active">
            <h2>Add a Question</h2>
            <form id="question-form">
                <div class="form-group">
                    <label for="question-type">Question Type:</label>
                    <select id="question-type">
                        <option value="multiple-choice">Multiple Choice</option>
                        <option value="identification">Identification</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="question-text">Question:</label>
                    <input type="text" id="question-text" placeholder="Enter your question" required>
                </div>
                <div id="options-container">
                    <div class="form-group">
                        <label>Options:</label>
                        <input type="text" class="option-input" placeholder="Option A" required>
                        <input type="text" class="option-input" placeholder="Option B" required>
                        <input type="text" class="option-input" placeholder="Option C" required>
                        <input type="text" class="option-input" placeholder="Option D" required>
                    </div>
                    <div class="form-group">
                        <label for="correct-option">Correct Option (A, B, C, or D):</label>
                        <input type="text" id="correct-option" placeholder="e.g., A" required>
                    </div>
                </div>
                <div id="identification-container" class="hidden">
                    <div class="form-group">
                        <label for="correct-answer">Correct Answer:</label>
                        <input type="text" id="correct-answer" placeholder="Enter the correct answer">
                    </div>
                </div>
                <button type="submit" class="btn btn-primary">Save Question</button>
            </form>

            <h2>Question Bank (<span id="question-count">0</span>)</h2>
            <div id="question-list"></div>
        </section>

        <!-- Quiz Section -->
        <section id="quiz-section" class="section hidden">
            <div id="quiz-start">
                <p>You have <span id="quiz-question-count">0</span> questions in your bank.</p>
                <button class="btn btn-primary" id="start-quiz-btn">Start Quiz</button>
            </div>
            <div id="quiz-active" class="hidden"></div>
            <div id="quiz-results" class="hidden"></div>
        </section>
    </div>
    <script src="script.js"></script>
</body>
</html>
// ===== Configuration =====
const API_KEY = "your-api-key-here";
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;

// ===== State =====
let questions = JSON.parse(localStorage.getItem("quizQuestions")) || [];
let currentQuizIndex = 0;
let score = 0;
let quizQuestions = [];

// ===== DOM Elements =====
const tabCreate = document.getElementById("tab-create");
const tabQuiz = document.getElementById("tab-quiz");
const createSection = document.getElementById("create-section");
const quizSection = document.getElementById("quiz-section");
const questionForm = document.getElementById("question-form");
const questionType = document.getElementById("question-type");
const optionsContainer = document.getElementById("options-container");
const identificationContainer = document.getElementById("identification-container");
const questionList = document.getElementById("question-list");
const questionCount = document.getElementById("question-count");
const quizQuestionCount = document.getElementById("quiz-question-count");
const startQuizBtn = document.getElementById("start-quiz-btn");
const quizStart = document.getElementById("quiz-start");
const quizActive = document.getElementById("quiz-active");
const quizResults = document.getElementById("quiz-results");

// ===== Tab Navigation =====
tabCreate.addEventListener("click", function () {
    tabCreate.classList.add("active");
    tabQuiz.classList.remove("active");
    createSection.classList.remove("hidden");
    createSection.classList.add("active");
    quizSection.classList.add("hidden");
    quizSection.classList.remove("active");
});

tabQuiz.addEventListener("click", function () {
    tabQuiz.classList.add("active");
    tabCreate.classList.remove("active");
    quizSection.classList.remove("hidden");
    quizSection.classList.add("active");
    createSection.classList.add("hidden");
    createSection.classList.remove("active");
    quizQuestionCount.textContent = questions.length;
});

// ===== Question Type Toggle =====
questionType.addEventListener("change", function () {
    if (this.value === "multiple-choice") {
        optionsContainer.classList.remove("hidden");
        identificationContainer.classList.add("hidden");
    } else {
        optionsContainer.classList.add("hidden");
        identificationContainer.classList.remove("hidden");
    }
});

// ===== Save Question =====
questionForm.addEventListener("submit", function (e) {
    e.preventDefault();

    const type = questionType.value;
    const text = document.getElementById("question-text").value.trim();

    let question = {
        id: Date.now(),
        type: type,
        question: text,
    };

    if (type === "multiple-choice") {
        const optionInputs = document.querySelectorAll(".option-input");
        const options = Array.from(optionInputs).map(function (input) {
            return input.value.trim();
        });
        const correctLetter = document.getElementById("correct-option").value.trim().toUpperCase();
        const letterIndex = correctLetter.charCodeAt(0) - 65;

        if (letterIndex < 0 || letterIndex > 3) {
            alert("Please enter A, B, C, or D for the correct option.");
            return;
        }

        question.options = options;
        question.answer = options[letterIndex];
    } else {
        const answer = document.getElementById("correct-answer").value.trim();
        if (!answer) {
            alert("Please enter the correct answer.");
            return;
        }
        question.answer = answer;
    }

    questions.push(question);
    saveQuestions();
    renderQuestionList();
    questionForm.reset();
    questionType.dispatchEvent(new Event("change"));
});

// ===== Save to localStorage =====
function saveQuestions() {
    localStorage.setItem("quizQuestions", JSON.stringify(questions));
}

// ===== Render Question List =====
function renderQuestionList() {
    questionCount.textContent = questions.length;
    quizQuestionCount.textContent = questions.length;

    if (questions.length === 0) {
        questionList.innerHTML = "<p>No questions yet. Add some above!</p>";
        return;
    }

    questionList.innerHTML = questions
        .map(function (q) {
            return `
                <div class="question-card">
                    <div class="question-card-header">
                        <span class="question-type-badge">${q.type}</span>
                        <button class="btn btn-delete" onclick="deleteQuestion(${q.id})">Delete</button>
                    </div>
                    <p><strong>${q.question}</strong></p>
                    <p>Answer: ${q.answer}</p>
                </div>
            `;
        })
        .join("");
}

// ===== Delete Question =====
function deleteQuestion(id) {
    questions = questions.filter(function (q) {
        return q.id !== id;
    });
    saveQuestions();
    renderQuestionList();
}

// ===== Initialize =====
renderQuestionList();
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
    line-height: 1.6;
}

.container {
    max-width: 700px;
    margin: 40px auto;
    padding: 20px;
}

h1 {
    text-align: center;
    color: #1a73e8;
    margin-bottom: 20px;
    font-size: 2rem;
}

h2 {
    margin: 20px 0 10px;
    color: #444;
}

.tabs {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.tab-btn {
    flex: 1;
    padding: 12px;
    border: none;
    background-color: #e0e0e0;
    color: #555;
    font-size: 1rem;
    cursor: pointer;
    border-radius: 8px;
    transition: background-color 0.2s;
}

.tab-btn.active {
    background-color: #1a73e8;
    color: white;
}

.tab-btn:hover {
    background-color: #c0c0c0;
}

.tab-btn.active:hover {
    background-color: #1557b0;
}

.section {
    background-color: white;
    padding: 24px;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.section.hidden {
    display: none;
}

.hidden {
    display: none;
}

.form-group {
    margin-bottom: 16px;
}

.form-group label {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    color: #555;
}

.form-group input,
.form-group select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 1rem;
    margin-bottom: 8px;
}

.form-group input:focus,
.form-group select:focus {
    outline: none;
    border-color: #1a73e8;
    box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
}

.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.2s;
}

.btn-primary {
    background-color: #1a73e8;
    color: white;
}

.btn-primary:hover {
    background-color: #1557b0;
}

.btn-delete {
    background-color: #ea4335;
    color: white;
    padding: 6px 12px;
    font-size: 0.85rem;
}

.btn-delete:hover {
    background-color: #c5221f;
}

.question-card {
    background-color: #f8f9fa;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 12px;
}

.question-card .question-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}

.question-card .question-type-badge {
    font-size: 0.75rem;
    padding: 2px 8px;
    border-radius: 4px;
    background-color: #e8f0fe;
    color: #1a73e8;
    font-weight: 600;
}

Your question creator is fully working. You can add both multiple-choice and identification questions, see them in your question bank, and delete them. Next up, you will build the quiz interface that lets you test yourself on these questions.

Build the Quiz Interface

Your question creator is working and saving data to localStorage. Now you need a way to actually use those questions.

In this step, you will build the quiz-taking interface that presents questions one at a time, checks answers, tracks your score, and shows a final result.

In this step, get ready to:
  • Build the quiz interface HTML with question display, answer inputs, and feedback areas.
  • Add JavaScript logic for answer checking, scoring, and quiz navigation.
  • Add a placeholder hint button that will connect to AI in the next step.
Create the quiz mode UI

The quiz interface needs to show one question at a time with either clickable options (for multiple-choice) or a text input (for identification). It also needs a progress indicator, a score tracker, and a results screen at the end.

  • In index.html, find the empty <div id="quiz-active" class="hidden"></div> line and replace it with the following quiz interface code:
<div id="quiz-active" class="hidden">
    <div class="quiz-header">
        <span id="quiz-progress">Question 1 of 5</span>
        <span id="quiz-score">Score: 0</span>
    </div>
    <div class="quiz-question">
        <h3 id="quiz-question-text"></h3>
        <div id="quiz-answers"></div>
    </div>
    <button class="btn btn-submit" id="submit-answer-btn">Submit Answer</button>
    <div id="quiz-feedback" class="hidden"></div>
    <button class="btn btn-next hidden" id="next-question-btn">Next Question</button>
    <div class="hint-section">
        <button class="btn btn-hint" id="hint-btn">Get AI Hint</button>
        <div id="hint-display" class="hidden"></div>
    </div>
</div>

What does this code do?

This builds the active quiz screen with several key areas:

  • quiz-header displays the current question number and running score.
  • quiz-answers is an empty container that JavaScript will fill with either clickable options or a text input.
  • quiz-feedback shows correct/incorrect feedback after each answer submission.
  • hint-section contains the AI hint button you will wire up in the next step.
  • Now find the empty <div id="quiz-results" class="hidden"></div> line just below and replace it with this results screen:
<div id="quiz-results" class="hidden">
    <h2>Quiz Complete!</h2>
    <p id="results-text"></p>
    <button class="btn btn-primary" id="restart-quiz-btn">Back to Start</button>
</div>

What does this code do?

This is the end-of-quiz screen. results-text will show the final score, and the restart button takes the learner back to the quiz start screen.

  • Save index.html.

Now add the CSS styles that make the quiz look polished.

  • In style.css, add the following styles at the bottom of the file:
.btn-submit {
    background-color: #34a853;
    color: white;
    margin-top: 12px;
}

.btn-submit:hover {
    background-color: #2d8f47;
}

.btn-next {
    background-color: #1a73e8;
    color: white;
    margin-top: 12px;
}

.btn-hint {
    background-color: #f9ab00;
    color: white;
    margin-top: 16px;
}

.btn-hint:hover {
    background-color: #e09900;
}

.quiz-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
    font-weight: 600;
    color: #555;
}

.quiz-question h3 {
    margin-bottom: 16px;
    font-size: 1.2rem;
}

.quiz-option {
    display: block;
    padding: 12px 16px;
    margin-bottom: 8px;
    border: 1px solid #ddd;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.2s;
}

.quiz-option:hover {
    background-color: #f0f2f5;
}

.quiz-option.selected {
    border-color: #1a73e8;
    background-color: #e8f0fe;
}

.quiz-text-input {
    width: 100%;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 1rem;
}

#quiz-feedback {
    margin-top: 12px;
    padding: 12px;
    border-radius: 6px;
    font-weight: 600;
}

#quiz-feedback.correct {
    background-color: #e6f4ea;
    color: #137333;
}

#quiz-feedback.incorrect {
    background-color: #fce8e6;
    color: #c5221f;
}

.hint-section {
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid #e0e0e0;
}

#hint-display {
    margin-top: 12px;
    padding: 12px;
    background-color: #fef7e0;
    border-radius: 6px;
    border: 1px solid #f9ab00;
    color: #555;
}

#quiz-results {
    text-align: center;
}

#quiz-results h2 {
    color: #1a73e8;
    margin-bottom: 12px;
}

#results-text {
    font-size: 1.2rem;
    margin-bottom: 20px;
}

What does this code do?

These styles handle several visual elements:

  • .quiz-option makes each answer option a clickable card that highlights blue when selected.
  • #quiz-feedback.correct and #quiz-feedback.incorrect use green and red backgrounds for instant visual feedback.
  • #hint-display uses a warm yellow background to visually separate hints from regular content.
  • Save style.css and refresh your browser.
  • Click the Take Quiz tab. You should see the quiz start screen with a question count and a Start Quiz button.

Don't see the Start Quiz button?

Make sure you saved both index.html and style.css. Check that the new HTML is inside the <section id="quiz-section"> element and not outside it.

Help me debug my quiz interface layout.

Add answer checking and scoring

The HTML structure is ready, but clicking buttons does nothing yet. You need JavaScript to start the quiz, display questions, check answers, and track the score.

  • In script.js, find the line const quizResults = document.getElementById("quiz-results"); and add these new DOM references directly below it:
const quizProgress = document.getElementById("quiz-progress");
const quizScore = document.getElementById("quiz-score");
const quizQuestionText = document.getElementById("quiz-question-text");
const quizAnswers = document.getElementById("quiz-answers");
const submitAnswerBtn = document.getElementById("submit-answer-btn");
const quizFeedback = document.getElementById("quiz-feedback");
const nextQuestionBtn = document.getElementById("next-question-btn");
const hintBtn = document.getElementById("hint-btn");
const hintDisplay = document.getElementById("hint-display");
const resultsText = document.getElementById("results-text");
const restartQuizBtn = document.getElementById("restart-quiz-btn");

What does this code do?

Each const stores a reference to an HTML element so you can update it from JavaScript without searching the DOM every time. Caching these references upfront keeps your code fast and readable.

  • Now add the Start Quiz handler and the shuffle utility. Find the // ===== Initialize ===== comment at the bottom of the file and paste this code directly above it:
// ===== Start Quiz =====
startQuizBtn.addEventListener("click", function () {
    if (questions.length === 0) {
        alert("Add some questions first!");
        return;
    }

    quizQuestions = shuffleArray([...questions]);
    currentQuizIndex = 0;
    score = 0;

    quizStart.classList.add("hidden");
    quizResults.classList.add("hidden");
    quizActive.classList.remove("hidden");

    showQuestion();
});

// ===== Utility: Shuffle Array =====
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        const temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

What does this code do?

When the learner clicks Start Quiz, the handler first checks that questions exist. Then it shuffles a copy of the questions array so the order is different each time.

shuffleArray uses the Fisher-Yates algorithm, which swaps each element with a random earlier element to produce an unbiased shuffle.

  • Add the showQuestion and selectOption functions below the Start Quiz handler you just added:
// ===== Show Question =====
function showQuestion() {
    const q = quizQuestions[currentQuizIndex];

    quizProgress.textContent = `Question ${currentQuizIndex + 1} of ${quizQuestions.length}`;
    quizScore.textContent = `Score: ${score}`;
    quizQuestionText.textContent = q.question;

    quizFeedback.classList.add("hidden");
    nextQuestionBtn.classList.add("hidden");
    submitAnswerBtn.classList.remove("hidden");
    hintDisplay.classList.add("hidden");
    hintBtn.disabled = false;

    if (q.type === "multiple-choice") {
        quizAnswers.innerHTML = q.options
            .map(function (option) {
                return `<div class="quiz-option" onclick="selectOption(this)">${option}</div>`;
            })
            .join("");
    } else {
        quizAnswers.innerHTML = `<input type="text" class="quiz-text-input" placeholder="Type your answer here">`;
    }
}

// ===== Select Option =====
function selectOption(element) {
    const options = document.querySelectorAll(".quiz-option");
    options.forEach(function (opt) {
        opt.classList.remove("selected");
    });
    element.classList.add("selected");
}

What does this code do?

showQuestion reads the current question from the shuffled array and updates the page. For multiple-choice questions, it creates clickable option divs. For identification questions, it renders a text input.

selectOption removes the selected class from all options, then adds it to the one the learner clicked. This ensures only one option is highlighted at a time.

  • Add the answer submission handler below the selectOption function:
// ===== Submit Answer =====
submitAnswerBtn.addEventListener("click", function () {
    const q = quizQuestions[currentQuizIndex];
    let userAnswer = "";

    if (q.type === "multiple-choice") {
        const selected = document.querySelector(".quiz-option.selected");
        if (!selected) {
            alert("Please select an answer.");
            return;
        }
        userAnswer = selected.textContent;
    } else {
        const input = document.querySelector(".quiz-text-input");
        userAnswer = input.value.trim();
        if (!userAnswer) {
            alert("Please type your answer.");
            return;
        }
    }

    const isCorrect = userAnswer.toLowerCase() === q.answer.toLowerCase();

    if (isCorrect) {
        score++;
        quizFeedback.textContent = "Correct!";
        quizFeedback.className = "correct";
    } else {
        quizFeedback.textContent = `Incorrect. The answer was: ${q.answer}`;
        quizFeedback.className = "incorrect";
    }

    quizFeedback.classList.remove("hidden");
    submitAnswerBtn.classList.add("hidden");
    hintBtn.disabled = true;

    if (currentQuizIndex < quizQuestions.length - 1) {
        nextQuestionBtn.classList.remove("hidden");
    } else {
        setTimeout(showResults, 1500);
    }
});

What does this code do?

This handler grabs the user's answer from either the selected option or the text input. It uses .toLowerCase() for case-insensitive comparison, so "Paris" and "paris" both count as correct.

After checking, it shows a green or red feedback message. If there are more questions, the Next button appears. If it was the last question, results display after a short delay.

  • Add the remaining navigation handlers below the Submit Answer handler:
// ===== Next Question =====
nextQuestionBtn.addEventListener("click", function () {
    currentQuizIndex++;
    showQuestion();
});

// ===== Show Results =====
function showResults() {
    quizActive.classList.add("hidden");
    quizResults.classList.remove("hidden");
    const percentage = Math.round((score / quizQuestions.length) * 100);
    resultsText.textContent = `You scored ${score} out of ${quizQuestions.length} (${percentage}%)`;
}

// ===== Restart Quiz =====
restartQuizBtn.addEventListener("click", function () {
    quizResults.classList.add("hidden");
    quizStart.classList.remove("hidden");
    quizQuestionCount.textContent = questions.length;
});

What does this code do?

nextQuestionBtn advances to the next question by incrementing the index and calling showQuestion() again.

showResults calculates the final percentage and displays the score. restartQuizBtn hides the results and brings back the start screen so the learner can quiz again.

  • Save script.js and refresh your browser.
  • Click the Take Quiz tab, then click Start Quiz. You should see your first question with clickable options or a text input.
  • Answer the question and click Submit Answer. You should see green or red feedback, and a Next Question button should appear.

Quiz not starting or showing errors?

Make sure you have at least one saved question in your question bank. The quiz requires questions to exist before it can start.

If you see a console error, check that all 11 new const DOM references match the id attributes in your HTML exactly. A single typo will cause a null reference error.

Help me debug why my quiz is not starting.

Add a Get Hint button

The hint button already exists in your HTML from the first substep. For now, you will add a placeholder click handler that shows a loading message. In the next step, you will replace this placeholder with a real AI call.

  • In script.js, add this placeholder hint handler above the // ===== Utility: Shuffle Array ===== comment:
// ===== AI Hint (placeholder) =====
hintBtn.addEventListener("click", async function () {
    hintDisplay.classList.remove("hidden");
    hintDisplay.textContent = "Loading hint...";
});

What does this code do?

This is a temporary placeholder. When the learner clicks Get AI Hint, it reveals the hint display area and shows a loading message. The async keyword is already in place because the real version will make an API call.

  • Save script.js and refresh your browser.
  • Start a quiz and click Get AI Hint. You should see a yellow box appear below the hint button with the text "Loading hint..." inside it.

Hint button not doing anything?

Check that the hintBtn variable references hint-btn (with a hyphen, not an underscore). Also confirm the hint handler is placed after the DOM reference declarations.

Help me debug why my hint button is not working.

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files before moving on.

ⓧ I'd like to double check the full code

Here is the full code for each file at this point in the project.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Study Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Study Quiz App</h1>
        <nav class="tabs">
            <button class="tab-btn active" id="tab-create">Create Questions</button>
            <button class="tab-btn" id="tab-quiz">Take Quiz</button>
        </nav>

        <!-- Create Questions Section -->
        <section id="create-section" class="section active">
            <h2>Add a Question</h2>
            <form id="question-form">
                <div class="form-group">
                    <label for="question-type">Question Type:</label>
                    <select id="question-type">
                        <option value="multiple-choice">Multiple Choice</option>
                        <option value="identification">Identification</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="question-text">Question:</label>
                    <input type="text" id="question-text" placeholder="Enter your question" required>
                </div>
                <div id="options-container">
                    <div class="form-group">
                        <label>Options:</label>
                        <input type="text" class="option-input" placeholder="Option A" required>
                        <input type="text" class="option-input" placeholder="Option B" required>
                        <input type="text" class="option-input" placeholder="Option C" required>
                        <input type="text" class="option-input" placeholder="Option D" required>
                    </div>
                    <div class="form-group">
                        <label for="correct-option">Correct Option (A, B, C, or D):</label>
                        <input type="text" id="correct-option" placeholder="e.g., A" required>
                    </div>
                </div>
                <div id="identification-container" class="hidden">
                    <div class="form-group">
                        <label for="correct-answer">Correct Answer:</label>
                        <input type="text" id="correct-answer" placeholder="Enter the correct answer">
                    </div>
                </div>
                <button type="submit" class="btn btn-primary">Save Question</button>
            </form>

            <h2>Question Bank (<span id="question-count">0</span>)</h2>
            <div id="question-list"></div>
        </section>

        <!-- Quiz Section -->
        <section id="quiz-section" class="section hidden">
            <div id="quiz-start">
                <p>You have <span id="quiz-question-count">0</span> questions in your bank.</p>
                <button class="btn btn-primary" id="start-quiz-btn">Start Quiz</button>
            </div>
            <div id="quiz-active" class="hidden">
                <div class="quiz-header">
                    <span id="quiz-progress">Question 1 of 5</span>
                    <span id="quiz-score">Score: 0</span>
                </div>
                <div class="quiz-question">
                    <h3 id="quiz-question-text"></h3>
                    <div id="quiz-answers"></div>
                </div>
                <button class="btn btn-submit" id="submit-answer-btn">Submit Answer</button>
                <div id="quiz-feedback" class="hidden"></div>
                <button class="btn btn-next hidden" id="next-question-btn">Next Question</button>
                <div class="hint-section">
                    <button class="btn btn-hint" id="hint-btn">Get AI Hint</button>
                    <div id="hint-display" class="hidden"></div>
                </div>
            </div>
            <div id="quiz-results" class="hidden">
                <h2>Quiz Complete!</h2>
                <p id="results-text"></p>
                <button class="btn btn-primary" id="restart-quiz-btn">Back to Start</button>
            </div>
        </section>
    </div>
    <script src="script.js"></script>
</body>
</html>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
    line-height: 1.6;
}

.container {
    max-width: 700px;
    margin: 40px auto;
    padding: 20px;
}

h1 {
    text-align: center;
    color: #1a73e8;
    margin-bottom: 20px;
    font-size: 2rem;
}

h2 {
    margin: 20px 0 10px;
    color: #444;
}

.tabs {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.tab-btn {
    flex: 1;
    padding: 12px;
    border: none;
    background-color: #e0e0e0;
    color: #555;
    font-size: 1rem;
    cursor: pointer;
    border-radius: 8px;
    transition: background-color 0.2s;
}

.tab-btn.active {
    background-color: #1a73e8;
    color: white;
}

.tab-btn:hover {
    background-color: #c0c0c0;
}

.tab-btn.active:hover {
    background-color: #1557b0;
}

.section {
    background-color: white;
    padding: 24px;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.section.hidden {
    display: none;
}

.hidden {
    display: none;
}

.form-group {
    margin-bottom: 16px;
}

.form-group label {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    color: #555;
}

.form-group input,
.form-group select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 1rem;
    margin-bottom: 8px;
}

.form-group input:focus,
.form-group select:focus {
    outline: none;
    border-color: #1a73e8;
    box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
}

.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.2s;
}

.btn-primary {
    background-color: #1a73e8;
    color: white;
}

.btn-primary:hover {
    background-color: #1557b0;
}

.btn-submit {
    background-color: #34a853;
    color: white;
    margin-top: 12px;
}

.btn-submit:hover {
    background-color: #2d8f47;
}

.btn-next {
    background-color: #1a73e8;
    color: white;
    margin-top: 12px;
}

.btn-hint {
    background-color: #f9ab00;
    color: white;
    margin-top: 16px;
}

.btn-hint:hover {
    background-color: #e09900;
}

.btn-delete {
    background-color: #ea4335;
    color: white;
    padding: 6px 12px;
    font-size: 0.85rem;
}

.btn-delete:hover {
    background-color: #c5221f;
}

.question-card {
    background-color: #f8f9fa;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 12px;
}

.question-card .question-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}

.question-card .question-type-badge {
    font-size: 0.75rem;
    padding: 2px 8px;
    border-radius: 4px;
    background-color: #e8f0fe;
    color: #1a73e8;
    font-weight: 600;
}

.quiz-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
    font-weight: 600;
    color: #555;
}

.quiz-question h3 {
    margin-bottom: 16px;
    font-size: 1.2rem;
}

.quiz-option {
    display: block;
    padding: 12px 16px;
    margin-bottom: 8px;
    border: 1px solid #ddd;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.2s;
}

.quiz-option:hover {
    background-color: #f0f2f5;
}

.quiz-option.selected {
    border-color: #1a73e8;
    background-color: #e8f0fe;
}

.quiz-text-input {
    width: 100%;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 1rem;
}

#quiz-feedback {
    margin-top: 12px;
    padding: 12px;
    border-radius: 6px;
    font-weight: 600;
}

#quiz-feedback.correct {
    background-color: #e6f4ea;
    color: #137333;
}

#quiz-feedback.incorrect {
    background-color: #fce8e6;
    color: #c5221f;
}

.hint-section {
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid #e0e0e0;
}

#hint-display {
    margin-top: 12px;
    padding: 12px;
    background-color: #fef7e0;
    border-radius: 6px;
    border: 1px solid #f9ab00;
    color: #555;
}

#quiz-results {
    text-align: center;
}

#quiz-results h2 {
    color: #1a73e8;
    margin-bottom: 12px;
}

#results-text {
    font-size: 1.2rem;
    margin-bottom: 20px;
}
// ===== Configuration =====
const API_KEY = "your-api-key-here";
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;

// ===== State =====
let questions = JSON.parse(localStorage.getItem("quizQuestions")) || [];
let currentQuizIndex = 0;
let score = 0;
let quizQuestions = [];

// ===== DOM Elements =====
const tabCreate = document.getElementById("tab-create");
const tabQuiz = document.getElementById("tab-quiz");
const createSection = document.getElementById("create-section");
const quizSection = document.getElementById("quiz-section");
const questionForm = document.getElementById("question-form");
const questionType = document.getElementById("question-type");
const optionsContainer = document.getElementById("options-container");
const identificationContainer = document.getElementById("identification-container");
const questionList = document.getElementById("question-list");
const questionCount = document.getElementById("question-count");
const quizQuestionCount = document.getElementById("quiz-question-count");
const startQuizBtn = document.getElementById("start-quiz-btn");
const quizStart = document.getElementById("quiz-start");
const quizActive = document.getElementById("quiz-active");
const quizResults = document.getElementById("quiz-results");
const quizProgress = document.getElementById("quiz-progress");
const quizScore = document.getElementById("quiz-score");
const quizQuestionText = document.getElementById("quiz-question-text");
const quizAnswers = document.getElementById("quiz-answers");
const submitAnswerBtn = document.getElementById("submit-answer-btn");
const quizFeedback = document.getElementById("quiz-feedback");
const nextQuestionBtn = document.getElementById("next-question-btn");
const hintBtn = document.getElementById("hint-btn");
const hintDisplay = document.getElementById("hint-display");
const resultsText = document.getElementById("results-text");
const restartQuizBtn = document.getElementById("restart-quiz-btn");

// ===== Tab Navigation =====
tabCreate.addEventListener("click", function () {
    tabCreate.classList.add("active");
    tabQuiz.classList.remove("active");
    createSection.classList.remove("hidden");
    createSection.classList.add("active");
    quizSection.classList.add("hidden");
    quizSection.classList.remove("active");
});

tabQuiz.addEventListener("click", function () {
    tabQuiz.classList.add("active");
    tabCreate.classList.remove("active");
    quizSection.classList.remove("hidden");
    quizSection.classList.add("active");
    createSection.classList.add("hidden");
    createSection.classList.remove("active");
    quizQuestionCount.textContent = questions.length;
});

// ===== Question Type Toggle =====
questionType.addEventListener("change", function () {
    if (this.value === "multiple-choice") {
        optionsContainer.classList.remove("hidden");
        identificationContainer.classList.add("hidden");
    } else {
        optionsContainer.classList.add("hidden");
        identificationContainer.classList.remove("hidden");
    }
});

// ===== Save Question =====
questionForm.addEventListener("submit", function (e) {
    e.preventDefault();

    const type = questionType.value;
    const text = document.getElementById("question-text").value.trim();

    let question = {
        id: Date.now(),
        type: type,
        question: text,
    };

    if (type === "multiple-choice") {
        const optionInputs = document.querySelectorAll(".option-input");
        const options = Array.from(optionInputs).map(function (input) {
            return input.value.trim();
        });
        const correctLetter = document.getElementById("correct-option").value.trim().toUpperCase();
        const letterIndex = correctLetter.charCodeAt(0) - 65;

        if (letterIndex < 0 || letterIndex > 3) {
            alert("Please enter A, B, C, or D for the correct option.");
            return;
        }

        question.options = options;
        question.answer = options[letterIndex];
    } else {
        const answer = document.getElementById("correct-answer").value.trim();
        if (!answer) {
            alert("Please enter the correct answer.");
            return;
        }
        question.answer = answer;
    }

    questions.push(question);
    saveQuestions();
    renderQuestionList();
    questionForm.reset();
    questionType.dispatchEvent(new Event("change"));
});

// ===== Save to localStorage =====
function saveQuestions() {
    localStorage.setItem("quizQuestions", JSON.stringify(questions));
}

// ===== Render Question List =====
function renderQuestionList() {
    questionCount.textContent = questions.length;
    quizQuestionCount.textContent = questions.length;

    if (questions.length === 0) {
        questionList.innerHTML = "<p>No questions yet. Add some above!</p>";
        return;
    }

    questionList.innerHTML = questions
        .map(function (q) {
            return `
                <div class="question-card">
                    <div class="question-card-header">
                        <span class="question-type-badge">${q.type}</span>
                        <button class="btn btn-delete" onclick="deleteQuestion(${q.id})">Delete</button>
                    </div>
                    <p><strong>${q.question}</strong></p>
                    <p>Answer: ${q.answer}</p>
                </div>
            `;
        })
        .join("");
}

// ===== Delete Question =====
function deleteQuestion(id) {
    questions = questions.filter(function (q) {
        return q.id !== id;
    });
    saveQuestions();
    renderQuestionList();
}

// ===== Start Quiz =====
startQuizBtn.addEventListener("click", function () {
    if (questions.length === 0) {
        alert("Add some questions first!");
        return;
    }

    quizQuestions = shuffleArray([...questions]);
    currentQuizIndex = 0;
    score = 0;

    quizStart.classList.add("hidden");
    quizResults.classList.add("hidden");
    quizActive.classList.remove("hidden");

    showQuestion();
});

// ===== Show Question =====
function showQuestion() {
    const q = quizQuestions[currentQuizIndex];

    quizProgress.textContent = `Question ${currentQuizIndex + 1} of ${quizQuestions.length}`;
    quizScore.textContent = `Score: ${score}`;
    quizQuestionText.textContent = q.question;

    quizFeedback.classList.add("hidden");
    nextQuestionBtn.classList.add("hidden");
    submitAnswerBtn.classList.remove("hidden");
    hintDisplay.classList.add("hidden");
    hintBtn.disabled = false;

    if (q.type === "multiple-choice") {
        quizAnswers.innerHTML = q.options
            .map(function (option) {
                return `<div class="quiz-option" onclick="selectOption(this)">${option}</div>`;
            })
            .join("");
    } else {
        quizAnswers.innerHTML = `<input type="text" class="quiz-text-input" placeholder="Type your answer here">`;
    }
}

// ===== Select Option =====
function selectOption(element) {
    const options = document.querySelectorAll(".quiz-option");
    options.forEach(function (opt) {
        opt.classList.remove("selected");
    });
    element.classList.add("selected");
}

// ===== Submit Answer =====
submitAnswerBtn.addEventListener("click", function () {
    const q = quizQuestions[currentQuizIndex];
    let userAnswer = "";

    if (q.type === "multiple-choice") {
        const selected = document.querySelector(".quiz-option.selected");
        if (!selected) {
            alert("Please select an answer.");
            return;
        }
        userAnswer = selected.textContent;
    } else {
        const input = document.querySelector(".quiz-text-input");
        userAnswer = input.value.trim();
        if (!userAnswer) {
            alert("Please type your answer.");
            return;
        }
    }

    const isCorrect = userAnswer.toLowerCase() === q.answer.toLowerCase();

    if (isCorrect) {
        score++;
        quizFeedback.textContent = "Correct!";
        quizFeedback.className = "correct";
    } else {
        quizFeedback.textContent = `Incorrect. The answer was: ${q.answer}`;
        quizFeedback.className = "incorrect";
    }

    quizFeedback.classList.remove("hidden");
    submitAnswerBtn.classList.add("hidden");
    hintBtn.disabled = true;

    if (currentQuizIndex < quizQuestions.length - 1) {
        nextQuestionBtn.classList.remove("hidden");
    } else {
        setTimeout(showResults, 1500);
    }
});

// ===== Next Question =====
nextQuestionBtn.addEventListener("click", function () {
    currentQuizIndex++;
    showQuestion();
});

// ===== Show Results =====
function showResults() {
    quizActive.classList.add("hidden");
    quizResults.classList.remove("hidden");
    const percentage = Math.round((score / quizQuestions.length) * 100);
    resultsText.textContent = `You scored ${score} out of ${quizQuestions.length} (${percentage}%)`;
}

// ===== Restart Quiz =====
restartQuizBtn.addEventListener("click", function () {
    quizResults.classList.add("hidden");
    quizStart.classList.remove("hidden");
    quizQuestionCount.textContent = questions.length;
});

// ===== AI Hint (placeholder) =====
hintBtn.addEventListener("click", async function () {
    hintDisplay.classList.remove("hidden");
    hintDisplay.textContent = "Loading hint...";
});

// ===== Utility: Shuffle Array =====
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        const temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

// ===== Initialize =====
renderQuestionList();

Your quiz interface is fully functional. You can create questions, take quizzes, and get instant feedback on your answers. Next up, you will connect the hint button to the Gemini API so it delivers real AI-powered study hints.

Connect the AI Hint System

Your quiz app already lets you create questions, take quizzes, and see your score. The one missing piece is the "Get AI Hint" button, which currently just shows placeholder text.

In this step, you will connect the hint button to the Gemini API so it generates real, helpful hints. The AI will guide you toward the correct answer without revealing it directly.

In this step, get ready to:
  • Write an async function that calls the Gemini API with a carefully crafted prompt.
  • Replace the placeholder hint handler with real API logic and error handling.
  • Paste your API key and test the full hint flow on both question types.
Write the API call function

The hint feature needs a function that sends the current question to Gemini and gets back a helpful clue. This function will construct a prompt that tells the AI what the question is, what the correct answer is, and instructs it to hint without revealing.

  • In script.js, scroll down to the // ===== Utility: Shuffle Array ===== comment near the bottom of the file.
  • Add the following function directly above that comment:
async function getHint(question) {
    const prompt = `You are a study helper. A student is trying to answer the following question and needs a hint. Give a brief, helpful hint that guides them toward the answer WITHOUT directly revealing it. Keep your hint to 1-2 sentences.

Question: ${question.question}
${question.type === "multiple-choice" ? "Options: " + question.options.join(", ") : ""}
The correct answer is: ${question.answer}

Provide a hint that helps the student think about the answer without saying it directly.`;

    const response = await fetch(API_URL, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            contents: [
                {
                    parts: [{ text: prompt }],
                },
            ],
        }),
    });

    if (!response.ok) {
        throw new Error("API request failed with status " + response.status);
    }

    const data = await response.json();
    return data.candidates[0].content.parts[0].text;
}

What does this code do?

This function does three things:

  • It builds a prompt that tells Gemini the question, the correct answer, and asks for a non-revealing hint. Giving the AI the answer lets it craft a clue that points toward it without stating it outright.
  • It sends a POST request to the Gemini REST API using fetch(). The request body follows the contents format that Gemini expects.
  • It checks if the response was successful, then extracts the generated text from the nested response path.

What is prompt engineering?

The way you write instructions for an AI model is called prompt engineering. A good prompt is specific about the role ("You are a study helper"), the constraint ("without directly revealing it"), and the format ("1-2 sentences").

The better your prompt, the more useful the AI's response. You can experiment with the prompt text later to get different hint styles.

You won't see a change yet. The next substep wires this function to the hint button.

Connect the hint button to the API

The existing hint button handler just shows a static "Loading hint..." message. You need to replace it with a real handler that calls your new function, shows a loading state, and handles errors gracefully.

  • In script.js, find the placeholder hint handler section. It looks like this:
// ===== AI Hint (placeholder) =====
hintBtn.addEventListener("click", async function () {
    hintDisplay.classList.remove("hidden");
    hintDisplay.textContent = "Loading hint...";
});
  • Delete that entire block (the comment and the event listener).
  • Replace it with this new handler:
// ===== AI Hint =====
hintBtn.addEventListener("click", async function () {
    const q = quizQuestions[currentQuizIndex];

    hintBtn.disabled = true;
    hintDisplay.classList.remove("hidden");
    hintDisplay.textContent = "Thinking...";

    try {
        const hint = await getHint(q);
        hintDisplay.textContent = hint;
    } catch (error) {
        hintDisplay.textContent = "Sorry, could not get a hint right now. Try again later.";
        console.error("Hint error:", error);
    }
});

What does this handler do?

  • It grabs the current quiz question from quizQuestions[currentQuizIndex] so the hint is relevant to what the learner is looking at.
  • It disables the button and shows "Thinking..." so the user knows something is happening while the API responds.
  • The try/catch block handles the case where the network request fails. Instead of crashing, it shows a friendly error message.
  • Save script.js.

You won't see a working hint yet because the API key is still a placeholder. The next substep takes care of that.

Paste your API key and test the full flow

Your code references an API_KEY constant that still holds the placeholder value. You need to swap it for the real key you copied from Google AI Studio in Step 1.

  • In script.js, scroll to the very top of the file.
  • Find this line:
const API_KEY = "your-api-key-here";
  • Replace your-api-key-here with the actual API key you copied from Google AI Studio. Keep the quotes around it.
  • Save script.js.

Why is the API key in the code?

For a personal study tool, putting the key directly in client-side code is fine. The Gemini free tier has rate limits that prevent abuse.

In a production app you would hide the key on a backend server. For this portfolio project, the tradeoff keeps things simple so you can focus on learning the API integration pattern.

Now test the complete hint flow.

  • Refresh your browser to load the updated script.js.
  • Switch to the Create Questions tab.
  • Create a multiple-choice question (pick any topic you are studying).
  • Create an identification question.
  • Switch to the Take Quiz tab and click Start Quiz.
  • On any question, click Get AI Hint.

You should see "Thinking..." appear briefly, then a 1-2 sentence hint that nudges you toward the answer without giving it away.

  • Try the hint on both a multiple-choice and an identification question to confirm it works for each type.

Hint not appearing?

  • If you see the error message "Sorry, could not get a hint right now," open the browser Console (press F12, then click the Console tab). Look for the logged error. A 400 status usually means the API key is missing or malformed. Double-check you pasted the full key with no extra spaces.
  • If nothing happens at all when you click the button, make sure you saved script.js and refreshed the browser. Check the Console for syntax errors.
  • If the Console shows a CORS error or network error, check your internet connection. The Gemini API requires an active connection.

Still stuck? Help me debug why my Gemini API hint request is failing. I see this error in my console:

✔️ Awesome, I've got everything!

Great. Double check you have saved script.js with your API key pasted in.

ⓧ I'd like to double check the full code

Here is the complete script.js file as it should look at the end of this step. Remember that your-api-key-here should be replaced with your actual Gemini API key.

// ===== Configuration =====
const API_KEY = "your-api-key-here";
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;

// ===== State =====
let questions = JSON.parse(localStorage.getItem("quizQuestions")) || [];
let currentQuizIndex = 0;
let score = 0;
let quizQuestions = [];

// ===== DOM Elements =====
const tabCreate = document.getElementById("tab-create");
const tabQuiz = document.getElementById("tab-quiz");
const createSection = document.getElementById("create-section");
const quizSection = document.getElementById("quiz-section");
const questionForm = document.getElementById("question-form");
const questionType = document.getElementById("question-type");
const optionsContainer = document.getElementById("options-container");
const identificationContainer = document.getElementById("identification-container");
const questionList = document.getElementById("question-list");
const questionCount = document.getElementById("question-count");
const quizQuestionCount = document.getElementById("quiz-question-count");
const startQuizBtn = document.getElementById("start-quiz-btn");
const quizStart = document.getElementById("quiz-start");
const quizActive = document.getElementById("quiz-active");
const quizResults = document.getElementById("quiz-results");
const quizProgress = document.getElementById("quiz-progress");
const quizScore = document.getElementById("quiz-score");
const quizQuestionText = document.getElementById("quiz-question-text");
const quizAnswers = document.getElementById("quiz-answers");
const submitAnswerBtn = document.getElementById("submit-answer-btn");
const quizFeedback = document.getElementById("quiz-feedback");
const nextQuestionBtn = document.getElementById("next-question-btn");
const hintBtn = document.getElementById("hint-btn");
const hintDisplay = document.getElementById("hint-display");
const resultsText = document.getElementById("results-text");
const restartQuizBtn = document.getElementById("restart-quiz-btn");

// ===== Tab Navigation =====
tabCreate.addEventListener("click", function () {
    tabCreate.classList.add("active");
    tabQuiz.classList.remove("active");
    createSection.classList.remove("hidden");
    createSection.classList.add("active");
    quizSection.classList.add("hidden");
    quizSection.classList.remove("active");
});

tabQuiz.addEventListener("click", function () {
    tabQuiz.classList.add("active");
    tabCreate.classList.remove("active");
    quizSection.classList.remove("hidden");
    quizSection.classList.add("active");
    createSection.classList.add("hidden");
    createSection.classList.remove("active");
    quizQuestionCount.textContent = questions.length;
});

// ===== Question Type Toggle =====
questionType.addEventListener("change", function () {
    if (this.value === "multiple-choice") {
        optionsContainer.classList.remove("hidden");
        identificationContainer.classList.add("hidden");
    } else {
        optionsContainer.classList.add("hidden");
        identificationContainer.classList.remove("hidden");
    }
});

// ===== Save Question =====
questionForm.addEventListener("submit", function (e) {
    e.preventDefault();

    const type = questionType.value;
    const text = document.getElementById("question-text").value.trim();

    let question = {
        id: Date.now(),
        type: type,
        question: text,
    };

    if (type === "multiple-choice") {
        const optionInputs = document.querySelectorAll(".option-input");
        const options = Array.from(optionInputs).map(function (input) {
            return input.value.trim();
        });
        const correctLetter = document.getElementById("correct-option").value.trim().toUpperCase();
        const letterIndex = correctLetter.charCodeAt(0) - 65;

        if (letterIndex < 0 || letterIndex > 3) {
            alert("Please enter A, B, C, or D for the correct option.");
            return;
        }

        question.options = options;
        question.answer = options[letterIndex];
    } else {
        const answer = document.getElementById("correct-answer").value.trim();
        if (!answer) {
            alert("Please enter the correct answer.");
            return;
        }
        question.answer = answer;
    }

    questions.push(question);
    saveQuestions();
    renderQuestionList();
    questionForm.reset();
    questionType.dispatchEvent(new Event("change"));
});

// ===== Save to localStorage =====
function saveQuestions() {
    localStorage.setItem("quizQuestions", JSON.stringify(questions));
}

// ===== Render Question List =====
function renderQuestionList() {
    questionCount.textContent = questions.length;
    quizQuestionCount.textContent = questions.length;

    if (questions.length === 0) {
        questionList.innerHTML = "<p>No questions yet. Add some above!</p>";
        return;
    }

    questionList.innerHTML = questions
        .map(function (q) {
            return `
                <div class="question-card">
                    <div class="question-card-header">
                        <span class="question-type-badge">${q.type}</span>
                        <button class="btn btn-delete" onclick="deleteQuestion(${q.id})">Delete</button>
                    </div>
                    <p><strong>${q.question}</strong></p>
                    <p>Answer: ${q.answer}</p>
                </div>
            `;
        })
        .join("");
}

// ===== Delete Question =====
function deleteQuestion(id) {
    questions = questions.filter(function (q) {
        return q.id !== id;
    });
    saveQuestions();
    renderQuestionList();
}

// ===== Start Quiz =====
startQuizBtn.addEventListener("click", function () {
    if (questions.length === 0) {
        alert("Add some questions first!");
        return;
    }

    quizQuestions = shuffleArray([...questions]);
    currentQuizIndex = 0;
    score = 0;

    quizStart.classList.add("hidden");
    quizResults.classList.add("hidden");
    quizActive.classList.remove("hidden");

    showQuestion();
});

// ===== Show Question =====
function showQuestion() {
    const q = quizQuestions[currentQuizIndex];

    quizProgress.textContent = `Question ${currentQuizIndex + 1} of ${quizQuestions.length}`;
    quizScore.textContent = `Score: ${score}`;
    quizQuestionText.textContent = q.question;

    quizFeedback.classList.add("hidden");
    nextQuestionBtn.classList.add("hidden");
    submitAnswerBtn.classList.remove("hidden");
    hintDisplay.classList.add("hidden");
    hintBtn.disabled = false;

    if (q.type === "multiple-choice") {
        quizAnswers.innerHTML = q.options
            .map(function (option) {
                return `<div class="quiz-option" onclick="selectOption(this)">${option}</div>`;
            })
            .join("");
    } else {
        quizAnswers.innerHTML = `<input type="text" class="quiz-text-input" placeholder="Type your answer here">`;
    }
}

// ===== Select Option =====
function selectOption(element) {
    const options = document.querySelectorAll(".quiz-option");
    options.forEach(function (opt) {
        opt.classList.remove("selected");
    });
    element.classList.add("selected");
}

// ===== Submit Answer =====
submitAnswerBtn.addEventListener("click", function () {
    const q = quizQuestions[currentQuizIndex];
    let userAnswer = "";

    if (q.type === "multiple-choice") {
        const selected = document.querySelector(".quiz-option.selected");
        if (!selected) {
            alert("Please select an answer.");
            return;
        }
        userAnswer = selected.textContent;
    } else {
        const input = document.querySelector(".quiz-text-input");
        userAnswer = input.value.trim();
        if (!userAnswer) {
            alert("Please type your answer.");
            return;
        }
    }

    const isCorrect = userAnswer.toLowerCase() === q.answer.toLowerCase();

    if (isCorrect) {
        score++;
        quizFeedback.textContent = "Correct!";
        quizFeedback.className = "correct";
    } else {
        quizFeedback.textContent = `Incorrect. The answer was: ${q.answer}`;
        quizFeedback.className = "incorrect";
    }

    quizFeedback.classList.remove("hidden");
    submitAnswerBtn.classList.add("hidden");
    hintBtn.disabled = true;

    if (currentQuizIndex < quizQuestions.length - 1) {
        nextQuestionBtn.classList.remove("hidden");
    } else {
        setTimeout(showResults, 1500);
    }
});

// ===== Next Question =====
nextQuestionBtn.addEventListener("click", function () {
    currentQuizIndex++;
    showQuestion();
});

// ===== Show Results =====
function showResults() {
    quizActive.classList.add("hidden");
    quizResults.classList.remove("hidden");
    const percentage = Math.round((score / quizQuestions.length) * 100);
    resultsText.textContent = `You scored ${score} out of ${quizQuestions.length} (${percentage}%)`;
}

// ===== Restart Quiz =====
restartQuizBtn.addEventListener("click", function () {
    quizResults.classList.add("hidden");
    quizStart.classList.remove("hidden");
    quizQuestionCount.textContent = questions.length;
});

// ===== AI Hint =====
hintBtn.addEventListener("click", async function () {
    const q = quizQuestions[currentQuizIndex];

    hintBtn.disabled = true;
    hintDisplay.classList.remove("hidden");
    hintDisplay.textContent = "Thinking...";

    try {
        const hint = await getHint(q);
        hintDisplay.textContent = hint;
    } catch (error) {
        hintDisplay.textContent = "Sorry, could not get a hint right now. Try again later.";
        console.error("Hint error:", error);
    }
});

async function getHint(question) {
    const prompt = `You are a study helper. A student is trying to answer the following question and needs a hint. Give a brief, helpful hint that guides them toward the answer WITHOUT directly revealing it. Keep your hint to 1-2 sentences.

Question: ${question.question}
${question.type === "multiple-choice" ? "Options: " + question.options.join(", ") : ""}
The correct answer is: ${question.answer}

Provide a hint that helps the student think about the answer without saying it directly.`;

    const response = await fetch(API_URL, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            contents: [
                {
                    parts: [{ text: prompt }],
                },
            ],
        }),
    });

    if (!response.ok) {
        throw new Error("API request failed with status " + response.status);
    }

    const data = await response.json();
    return data.candidates[0].content.parts[0].text;
}

// ===== Utility: Shuffle Array =====
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        const temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

// ===== Initialize =====
renderQuestionList();

Your quiz app now has real AI-powered hints. You have a fully functional study tool that creates questions, runs quizzes, checks answers, and gives intelligent clues when you are stuck. Next up, the Secret Mission challenges you to add a score history tracker so you can see your improvement over time.

Secret mission

Add a Score History Tracker

Your quiz app works great for one session, but how do you know if you're actually improving over time? In this extension, you'll add a score history tracker that records every quiz result and displays your progress with color-coded scores.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Your Gemini API key is on the free tier and does not incur charges.

Decide whether to keep your resources as they are, pause them to come back later, or delete them entirely.

Resources you used:

  • Local quiz-app project folder on your Desktop.
  • Gemini API key (free tier, stored in Google AI Studio).
  • localStorage data in Google Chrome (quiz questions and history).

Keep everything running

No action needed. Choose this if you plan to keep adding questions and using the app.

  • Your project files stay in the quiz-app folder on your Desktop.
  • Your Gemini API key remains active on the free tier with no charges.
  • Your saved questions and quiz history persist in your browser's localStorage.

Pause - I'll come back to this later

Since this project is local with no running processes, there is nothing to pause. Your files, API key, and localStorage data will all be waiting for you when you come back.

  • Close the browser tab running your quiz app.
  • Close VS Code when you are done.
  • Everything will be exactly as you left it next time you open the files.

Delete - I don't want to use this again

Remove all project resources and start fresh.

  • Delete your project folder by running this command in Terminal (macOS/Linux):
rm -rf ~/Desktop/quiz-app

On Windows, run this in PowerShell:

Remove-Item -Recurse -Force ~\Desktop\quiz-app
  • Clear your localStorage data by opening Chrome DevTools (press F12), clicking the Application tab, expanding Local Storage in the left sidebar, right-clicking your site entry, and selecting Clear.
  • Delete your Gemini API key by going to Google AI Studio. Click Get API Key in the left sidebar. Find your key in the list, click the trash icon next to it, and confirm deletion.

Nice Work!

Nice Work!

You just built a fully functional study quiz app from scratch using HTML, CSS, and JavaScript. Your app creates questions, quizzes you on them, and even calls a real AI to help when you get stuck.

You've learned how to:

  • Build a complete web application with a question creator supporting multiple-choice and identification question types.
  • Implement persistent data storage using localStorage so your question bank survives page refreshes.
  • Integrate a real AI API (Google Gemini) using fetch() to provide intelligent study hints through prompt engineering.
  • Secret Mission: Added a score history tracker that records quiz results over time with color-coded performance indicators.

Ready to quiz yourself?