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

> Learn to build a quiz app with timer and score tracking using vanilla JavaScript CSS and HTML Explore the jisan-mia/dom-projects repository for a full example.

- Repository: [Jisan Mia/dom-projects](https://github.com/jisan-mia/dom-projects)
- Tags: tutorial
- Published: 2026-03-04

---

**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`](https://github.com/jisan-mia/dom-projects/blob/main/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`](https://github.com/jisan-mia/dom-projects/blob/main/script.js) and uses `setInterval` to update both a numeric counter and a visual gauge every second:

```javascript
// 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:

```javascript
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`:

```javascript
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:

```javascript
// 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:

```javascript
// 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`](https://github.com/jisan-mia/dom-projects/blob/main/projects/simple-quiz-app/index.html) | Static structure for start screen, quiz interface, and results |
| [`projects/simple-quiz-app/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/simple-quiz-app/style.css) | Timer gauge (`#timeGauge`) and progress dot (`.prog`) styling |
| [`projects/simple-quiz-app/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/simple-quiz-app/script.js) | Core logic: `renderCounter`, `checkAnswer`, `scoreRender` |
| [`projects/advanced-quiz-app/index.html`](https://github.com/jisan-mia/dom-projects/blob/main/projects/advanced-quiz-app/index.html) | Multi-page UI with home, game, end, and high-score screens |
| [`projects/advanced-quiz-app/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/advanced-quiz-app/style.css) | Progress bar and high-score table styling |
| [`projects/advanced-quiz-app/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/advanced-quiz-app/script.js) | API integration, `localStorage` persistence, high-score sorting |
| [`data.json`](https://github.com/jisan-mia/dom-projects/blob/main/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`](https://github.com/jisan-mia/dom-projects/blob/main/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.