Create a Speed Typing Game with HTML, CSS, and JavaScript


 Learn how to build an exciting speed typing game using HTML, CSS, and JavaScript. Follow our step-by-step guide to create your own fun and interactive typing challenge.

Create a Speed Typing Game with HTML, CSS, and JavaScript

Welcome to our in-depth tutorial on crafting an engaging speed typing game using HTML, CSS, and JavaScript.

Throughout this tutorial, we'll guide you through the process of conceptualizing and building a fully functional typing game that will put your users' typing skills to the test. Whether you're a novice coder eager to learn the ropes or a seasoned developer looking for a fun project, this guide has something for everyone.

You'll learn how to structure your game using HTML, style it to perfection with CSS, and bring it to life with the dynamic functionality of JavaScript. By the end of this journey, you'll not only have a polished typing game under your belt but also a deeper understanding of how to leverage these three core web technologies to create interactive and engaging experiences.

So, buckle up and get ready to dive into the exciting world of game development with HTML, CSS, and JavaScript. Let's begin this adventure together and unleash your creativity!

Source Code

Step 1 (HTML Code):

To start building our speed typing game, let's create the initial HTML structure. Below is a breakdown of each key component:

1. `<!DOCTYPE html>`: This declaration specifies the document type and version of HTML being used, which is HTML5.

2. `<html lang="en">`: The opening tag for the HTML document, specifying that it is written in English ("en").

3. `<head>`: This section contains meta-information about the document, such as the character set, title, and links to external resources.

   - `<title>Speed Typing Test</title>`: Sets the title of the web page to "Speed Typing Test."

   - `<meta charset="UTF-8" />`: Specifies the character encoding for the document as UTF-8.

   - `<meta name="viewport" content="width=device-width" />`: Configures the viewport settings for responsive design.

   - `<link rel="stylesheet" href="styles.css" />`: Links an external CSS file named "styles.css" to apply styles to the HTML elements.

4. `<body>`: This section contains the main content of the HTML document.

   - `<main>`: The <main> element encapsulates the primary content of the page.

   - `<div class="quote-display" id="quoteDisplay"><span></span></div>`: A container for displaying the quote that users will type. It has a class "quote-display" and an ID "quoteDisplay."

   - `<section class="typing">`: This section contains elements related to the typing test.

   - `<hr />`: This horizontal rule creates a visual separation between sections.

   - `<div class="score">`: A container for displaying the user's score.

   - `<div class="timer">`: A container for displaying the timer for the typing test.

     - `<span class="timer__label">Time:</span>`: Label for the timer.

     - `<span class="timer__text" id="timer">0</span>`: The actual timer display, initially set to 0.

   - `<div class="words-per-minute">`: A container for displaying the words per minute (WPM) count.

     - `<span class="words-per-minute__label">Words/min:</span>`: Label for the WPM count.

     - `<span class="words-per-minute__text" id="wpm">0</span>`: The actual WPM count, initially set to 0.

   - `<textarea class="quote-input" name="quoteInput" id="quoteInput" placeholder="Start typing right here!" cdkFocusInitial></textarea>`: A text input field where users type the quote. It has a placeholder text and initial focus attribute.

5. `<script src="script.js"></script>`: This line includes an external JavaScript file named "script.js" to add interactivity and functionality to the webpage.

This HTML structure forms the foundation of our speed typing game. In the subsequent steps, we'll apply CSS styling and JavaScript functionality to bring our game to life. Let's proceed to style our game using CSS.

<!DOCTYPE html>
<html lang="en">
    <title>Speed Typing Test</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="stylesheet" href="styles.css" />
      <div class="quote-display" id="quoteDisplay"><span></span></div>
      <section class="typing">
        <hr />
        <div class="score">
          <div class="timer">
            <span class="timer__label">Time:</span>
            <span class="timer__text" id="timer">0</span>
            <span class="words-per-minute__label">Words/min:</span>
            <span class="words-per-minute__text" id="wpm">0</span>
        <textarea class="quote-input" name="quoteInput" id="quoteInput" placeholder="Start typing right here!" cdkFocusInitial></textarea>
    <script src="script.js"></script>

Step 2 (CSS Code):

Now, let's delve into the CSS styling for our speed typing game. Below, we'll break down each section of the CSS code:

1. Font Import: 

   - `@import url(;`: This line imports the "Gotu" font from Google Fonts to be used in the webpage.

2. Universal Selectors:

   - `*, *::before, *::after`: These selectors target all elements, including pseudo-elements, and apply a reset style to remove default margins and paddings while ensuring proper box-sizing behavior.

3. Body Styling:

   - `body`: Styles the <body> element, setting its width and height to 100% of the viewport, a white background color, and a default text color of gray.

4. Main Styling:

   - `main`: Styles the <main> element, utilizing CSS Grid to create a two-row layout. It sets padding around the main content area.

5. Quote Display Styling:

   - `.quote-display`: Styles the element with the class "quote-display," positioning it relatively within the grid and formatting the quote text.

6. Typing Section Styling:

   - `.typing`: Styles the section with the class "typing," positioning it relatively within the grid and creating a layout with CSS Grid. It also includes styling for interactive elements within this section.

7. Horizontal Rule Styling:

   - `hr`: Styles horizontal rule elements, positioning them across all columns and applying border styles for visual effects, particularly when hovered over.

8. Score Styling:

   - `.score`: Styles the element with the class "score," giving it a background, box-shadow, and padding to resemble a card-like appearance.

9. Quote Input Styling:

   - `.quote-input`: Styles the textarea input field for typing the quote, setting its width, height, padding, background color, border, and placeholder text color.

10. Text Styling for Correct, Incorrect, and Help Responses:

   - `.right`, `.wrong`, `.halp`: Styles elements with classes "right," "wrong," and "halp," providing specific text decorations and colors for each response type.

11. Keyframe Animation for Glitch Effect:

   - `@keyframes glitch`: Defines a keyframe animation called "glitch" to create a glitchy visual effect for elements with the class ".halp."

These CSS rules contribute to the visual appeal and interactivity of our speed typing game, enhancing the user experience. Save this CSS code in a file named styles.css to apply the styles to our HTML structure. Remember to link the CSS file to your HTML document for the styles to take effect.

@import url(;
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Gotu", sans-serif;
  font-weight: normal;
  font-size: 18px;

body {
  width: 100vw;
  height: 100vh;
  background: #fff;
  color: #414244;

main {
  display: grid;
  grid-template-rows: repeat(2, 1fr);
  width: 100%;
  height: 100%;
  padding: 3rem;

.quote-display {
  position: relative;
  grid-row: 1/2;
  align-self: end;
.quote-display span {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.4;
  letter-spacing: 0.1rem;
.quote-display::before {
  content: open-quote;
  position: absolute;
  top: -5rem;
  left: -2rem;
  z-index: -1;
  font-size: 10rem;
  color: #d7d7d8;

.typing {
  position: relative;
  grid-row: 2/-1;
  display: grid;
  grid-template-columns: 33% auto 2rem auto 2rem;
  align-items: start;
  width: 100%;
  height: 100%;
.typing:hover hr {
  width: 75%;

hr {
  grid-column: 1/-1;
  margin-top: 1rem;
  border: 0.25rem solid #4fb578;
  width: 25%;
  transition: width 300ms ease-in-out;
  outline: 0;

.score {
  position: relative;
  grid-column: 3/-2;
  display: flex;
  justify-content: space-between;
  z-index: 2;
  width: 100%;
  margin-top: -1.375rem;
  margin-bottom: -1rem;
  background: #fff;
  padding: 0.25rem 2rem;
  box-shadow: 0 3px 6px rgba(52, 31, 97, 0.16), 0 3px 6px rgba(52, 31, 97, 0.23);

.quote-input {
  grid-column: 2/-1;
  height: calc(50vh - 4rem);
  width: 100%;
  padding: 0 1rem;
  padding-top: 2rem;
  background: #fff;
  border: 0;
  border-radius: 0.2rem;
  resize: none;
  outline: none;
  transition: border 300ms ease-in-out;
.quote-input::placeholder {
  color: #d7d7d8;
  transition: color 300ms ease-in-out;
.quote-input:hover::placeholder {
  color: #4fb578;

.right {
  color: #4fb578;

.wrong {
  color: #b55648;
  text-decoration: line-through;

.halp {
  position: relative;
  border-color: #b55648;
  animation: glitch 300ms;

@keyframes glitch {
  0% {
    transform: translateY(-2%);
    border-color: #414244;
    box-shadow: -1.5px 2.5px #4fa5b5;
  15% {
    transform: translateY(5%);
    box-shadow: 2px -1px #d87dbb;
  45% {
    border-color: #d7d7d8;
    box-shadow: 0.5px -2px #d7d7d8;
  85% {
    border-color: #b55648;
    box-shadow: 1px -1.5px #d87dbb;
  100% {
    transform: translateX(-1%);
    box-shadow: -0.5px 0.5px #4fa5b5;

Step 3 (JavaScript Code):

Let's delve into the JavaScript code for our speed typing game. Below, we'll break down each section of the code:

1. Constants and DOM Element Selection:

   - The code defines a constant `RANDOM_QUOTE_API_URL` containing the URL for an API that provides random quotes.

   - It selects various HTML elements from the DOM using `document.querySelector()`, including elements for displaying quotes, the input field, timer, WPM, and a divider.

2. Event Listener:

   - An event listener is added to the `quoteInputElement` to listen for the "input" event, which triggers whenever the user types in the input field.

3. Input Handling:

   - Inside the event listener, the code retrieves an array of span elements representing individual characters in the displayed quote.

   - It then compares each typed character with the corresponding character in the quote, adding CSS classes to indicate correctness and updating a count of correctly typed characters.

4. Words Per Minute (WPM) Calculation:

   - The WPM is calculated based on the number of correctly typed characters and the time elapsed using a formula that considers 5 characters per word and adjusts for time.

5. Displaying WPM:

   - The calculated WPM is updated in the `wordsPerMinuteElement`.

6. Fetching a New Random Quote:

   - If the user types the entire quote correctly, the `getNextQuote()` function is called to fetch and display a new random quote.

7. Functions for Fetching and Displaying Quotes:

   - `getRandomQuote()`: Fetches a random quote from the API.

   - `getNextQuote()`: Calls `getRandomQuote()` to fetch a new quote, clears the previous quote display, and displays the new quote.

8. Timer Functions:

   - `startTimer()`: Initializes a timer to track how long the user takes to type the quote.

   - `getTimerTime()`: Calculates the time elapsed since the timer was started.

9. Initial Quote Display:

   - Calls `getNextQuote()` to fetch and display the first random quote when the page loads.

Save this JavaScript code in a file named script.js and link it to your HTML document. This script will add interactivity and functionality to your speed typing game, allowing users to type quotes, track their typing speed, and fetch new quotes to continue playing.

const quoteDisplayElement = document.querySelector("#quoteDisplay");
const quoteInputElement = document.querySelector("#quoteInput");
const timerElement = document.querySelector("#timer");
const wordsPerMinuteElement = document.querySelector("#wpm");
const divider = document.querySelector("hr");

quoteInputElement.addEventListener("input", () => {
  const quoteArray = quoteDisplayElement.querySelectorAll("span");
  const valueArray = quoteInputElement.value.split("");
  let right = true;
  let count = 0;

  quoteArray.forEach((characterSpan, i) => {
    const character = valueArray[i];

    if (character == null) {
      right = false;
    } else if (character === characterSpan.textContent) {
    } else {
      right = false;

  let randomNumber = Math.round(count * 60  / (getTimerTime() * 5) * 10) / 10;

  if (isNaN(randomNumber)) {
    wordsPerMinuteElement.textContent = "0";
  } else {
    wordsPerMinuteElement.textContent = randomNumber;

  if (right) getNextQuote();

function getRandomQuote() {
    .then((response) => response.json())
    .then((data) => data.content)
    .catch(error => console.log(error));

async function getNextQuote() {
  const quote = await getRandomQuote();
  quoteDisplayElement.innerHTML = "";
  quote.split("").forEach((character) => {
    const characterSpan = document.createElement("span");
    characterSpan.textContent = character;
  quoteInputElement.value = null;

let startTime;
function startTimer() {
  timerElement.textContent = 0;
  startTime = new Date();
  setInterval(() => {
    timer.textContent = getTimerTime();
  }, 1000)

function getTimerTime() {
  return Math.floor((new Date() - startTime)