How to Implement a Quiz App with Timer and Score Tracking in Vanilla JavaScript

You can build a fully functional quiz application with per-question timers, visual progress indicators, and persistent score tracking using only vanilla JavaScript, CSS, and HTML, as demonstrated in the jisan-mia/dom-projects repository.

The jisan-mia/dom-projects repository contains two complete implementations that show how to implement a quiz app with timer and score tracking without external dependencies. Both versions use plain DOM manipulation, setInterval for countdown logic, and localStorage for data persistence, making them suitable for static hosting or educational purposes.

Core Architecture of the Quiz Implementations

The repository provides two distinct approaches located in separate directories:

Component Simple Quiz (projects/simple-quiz-app/) Advanced Quiz (projects/advanced-quiz-app/)
Timer Per-question countdown using setInterval in script.js Enhanced timer with configurable duration
Progress Visual dots (.prog class) colored green/red per question Progress bar (#progressBarFull) showing percentage complete
Scoring Integer counter converted to percentage in scoreRender() Real-time score display with localStorage persistence
Data Source Hardcoded questions array Dynamic fetch from Open Trivia DB API

Building the Simple Quiz App with Timer and Score Tracking

The simple quiz implementation in projects/simple-quiz-app/ provides the minimal viable architecture for timed assessments.

Implementing the Per-Question Timer

The timer logic resides in script.js and uses setInterval to update both a numeric counter and a visual gauge every second:

// Timer configuration constants
const questionTime = 10; // seconds per question
const gaugeWidth = 300;  // pixels
const gaugeUnit = gaugeWidth / questionTime;

let TIMER = setInterval(renderCounter, 1000);

function renderCounter() {
    if (count <= questionTime) {
        counter.innerHTML = count;
        timeGauge.style.width = count * gaugeUnit + "px";
        count++;
    } else {
        // Time expired: mark as wrong and advance
        count = 0;
        answerIsWrong();
        moveToNextQuestionOrEnd();
    }
}

When the counter exceeds questionTime, the application automatically marks the answer as incorrect and advances to the next question, ensuring the quiz maintains its timing constraints.

Tracking and Displaying the Score

Score tracking occurs in the checkAnswer function, which compares the user's selection against the correct property of the current question object:

function checkAnswer(answer) {
    if (answer == questions[runningQuestion].correct) {
        score++;           // Increment score
        answerIsCorrect(); // Visual feedback: green dot
    } else {
        answerIsWrong();   // Visual feedback: red dot
    }
    count = 0; // Reset timer for next question
    moveToNextQuestionOrEnd();
}

The final percentage calculation and display logic resides in scoreRender:

function scoreRender() {
    const scorePercent = Math.round(100 * (score / questions.length));
    
    // Select performance-based image
    const scoreImg = scorePercent >= 80 ? "img/5.png"
                     : scorePercent >= 60 ? "img/4.png"
                     : scorePercent >= 40 ? "img/3.png"
                     : scorePercent >= 20 ? "img/2.png"
                     : "img/1.png";

    scoreDiv.innerHTML = `<img src="${scoreImg}" /> <p>${scorePercent}%</p>`;
    scoreDiv.style.display = "block";
}

Visual Progress Indicators

The simple quiz uses a series of <div> elements with the class prog to create progress dots. The functions answerIsCorrect and answerIsWrong modify the background color of the current dot to green (#0f0) or red (#f00), providing immediate visual feedback on performance.

Extending to the Advanced Quiz App

The projects/advanced-quiz-app/ directory contains a more sophisticated implementation that builds upon the simple quiz foundation.

Fetching Questions from Open Trivia DB

Unlike the hardcoded array in the simple version, the advanced quiz dynamically retrieves questions from the Open Trivia Database API:

// Configuration for API request
const API_URL = `https://opentdb.com/api.php?amount=${questionAmount}&category=${category}&difficulty=${difficulty}&type=multiple`;

fetchQuestions = async () => {
    const response = await fetch(API_URL);
    const loadedQuestions = await response.json();
    // Process and randomize answer order
    questions = loadedQuestions.results.map(processQuestionData);
};

Enhanced Progress Tracking and localStorage Persistence

The advanced version replaces the dot indicators with a continuous progress bar and implements persistent high-score storage:

// Progress bar update
progressBarFull.style.width = `${(questionCounter / maxQuestions) * 100}%`;

// Score persistence
localStorage.setItem('mostRecentScore', score);

// High score management
const highScores = JSON.parse(localStorage.getItem('highScores')) || [];
highScores.push({ name: username, score: mostRecentScore });
highScores.sort((a, b) => b.score - a.score);
highScores.splice(5); // Keep top 5
localStorage.setItem('highScores', JSON.stringify(highScores));

Key Implementation Files

File Path Purpose
projects/simple-quiz-app/index.html Static structure for start screen, quiz interface, and results
projects/simple-quiz-app/style.css Timer gauge (#timeGauge) and progress dot (.prog) styling
projects/simple-quiz-app/script.js Core logic: renderCounter, checkAnswer, scoreRender
projects/advanced-quiz-app/index.html Multi-page UI with home, game, end, and high-score screens
projects/advanced-quiz-app/style.css Progress bar and high-score table styling
projects/advanced-quiz-app/script.js API integration, localStorage persistence, high-score sorting
data.json Project metadata documenting timer and score features

Summary

  • Timer Implementation: Use setInterval with a counter variable and visual gauge updated every second; automatically advance questions when time expires.
  • Score Tracking: Maintain an integer counter incremented on correct answers, then convert to a percentage for final display with performance-based visual feedback.
  • Progress Indicators: Implement either dot-based (simple) or continuous bar (advanced) visualizers that update after each answer.
  • Data Persistence: Use localStorage to store recent scores and high-score tables across browser sessions, as shown in the advanced implementation.
  • Dynamic Content: Fetch questions from external APIs like Open Trivia DB and randomize answer order to create varied quiz experiences.

Frequently Asked Questions

How do you implement a countdown timer for each question in a JavaScript quiz?

Use setInterval to decrement or increment a counter every second while updating a visual gauge element. In projects/simple-quiz-app/script.js, the renderCounter function checks if the count exceeds questionTime (10 seconds); if so, it calls answerIsWrong() and advances to the next question, ensuring the quiz maintains strict timing per question.

What is the best way to track and calculate quiz scores in vanilla JavaScript?

Maintain a global score variable initialized to zero, increment it within your answer validation logic when the user's selection matches the correct answer, then calculate the percentage by dividing by the total questions. The scoreRender function in the simple quiz demonstrates this by computing Math.round(100 * (score / questions.length)) and displaying a performance-based image alongside the percentage.

How can you persist high scores across browser sessions in a quiz application?

Use the Web Storage API to save scores to localStorage. In the advanced quiz implementation, when the game ends, the code executes localStorage.setItem('mostRecentScore', score) to save the current attempt. For high-score tables, retrieve existing scores with JSON.parse(localStorage.getItem('highScores')), append the new result, sort by score in descending order, limit to the top entries (e.g., 5), and write the array back to localStorage using JSON.stringify.

What are the differences between the simple and advanced quiz implementations in the dom-projects repository?

The simple quiz (projects/simple-quiz-app/) uses a hardcoded questions array, visual progress dots that change color based on correctness, and a basic timer gauge, making it ideal for learning fundamental DOM manipulation. The advanced quiz (projects/advanced-quiz-app/) fetches questions dynamically from the Open Trivia DB API, features a continuous progress bar, implements localStorage for high-score persistence, and includes a multi-page interface with category/difficulty selection, demonstrating production-ready patterns for scalable quiz applications.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →