├── LICENSE ├── README.md ├── game.js ├── images ├── character_coloring.png ├── demonstration.gif └── html_css_styling.png ├── index.html └── style.css /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bash Overflow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-simple-typing-game 2 | 3 | This is a typing game created using HTML, CSS and JavaScript that can be run in the browser. 4 | 5 | A guide was written for the GeeksForGeeks Technical Scripter contest where the process is described and the code is explained. Check out: [Design a typing speed test game using JavaScript](https://www.geeksforgeeks.org/design-a-typing-speed-test-game-using-javascript/) 6 | 7 | ## Demonstration 8 | 9 | ![Preview](images/demonstration.gif "Demonstration GIF") 10 | -------------------------------------------------------------------------------- /game.js: -------------------------------------------------------------------------------- 1 | // define the time limit 2 | let TIME_LIMIT = 60; 3 | 4 | // define quotes to be used 5 | let quotes_array = [ 6 | "Push yourself, because no one else is going to do it for you.", 7 | "Failure is the condiment that gives success its flavor.", 8 | "Wake up with determination. Go to bed with satisfaction.", 9 | "It's going to be hard, but hard does not mean impossible.", 10 | "Learning never exhausts the mind.", 11 | "The only way to do great work is to love what you do." 12 | ]; 13 | 14 | // selecting required elements 15 | let timer_text = document.querySelector(".curr_time"); 16 | let accuracy_text = document.querySelector(".curr_accuracy"); 17 | let error_text = document.querySelector(".curr_errors"); 18 | let cpm_text = document.querySelector(".curr_cpm"); 19 | let wpm_text = document.querySelector(".curr_wpm"); 20 | let quote_text = document.querySelector(".quote"); 21 | let input_area = document.querySelector(".input_area"); 22 | let restart_btn = document.querySelector(".restart_btn"); 23 | let cpm_group = document.querySelector(".cpm"); 24 | let wpm_group = document.querySelector(".wpm"); 25 | let error_group = document.querySelector(".errors"); 26 | let accuracy_group = document.querySelector(".accuracy"); 27 | 28 | let timeLeft = TIME_LIMIT; 29 | let timeElapsed = 0; 30 | let total_errors = 0; 31 | let errors = 0; 32 | let accuracy = 0; 33 | let characterTyped = 0; 34 | let current_quote = ""; 35 | let quoteNo = 0; 36 | let timer = null; 37 | 38 | function updateQuote() { 39 | quote_text.textContent = null; 40 | current_quote = quotes_array[quoteNo]; 41 | 42 | // separate each character and make an element 43 | // out of each of them to individually style them 44 | current_quote.split('').forEach(char => { 45 | const charSpan = document.createElement('span') 46 | charSpan.innerText = char 47 | quote_text.appendChild(charSpan) 48 | }) 49 | 50 | // roll over to the first quote 51 | if (quoteNo < quotes_array.length - 1) 52 | quoteNo++; 53 | else 54 | quoteNo = 0; 55 | } 56 | 57 | function processCurrentText() { 58 | 59 | // get current input text and split it 60 | curr_input = input_area.value; 61 | curr_input_array = curr_input.split(''); 62 | 63 | // increment total characters typed 64 | characterTyped++; 65 | 66 | errors = 0; 67 | 68 | quoteSpanArray = quote_text.querySelectorAll('span'); 69 | quoteSpanArray.forEach((char, index) => { 70 | let typedChar = curr_input_array[index] 71 | 72 | // characters not currently typed 73 | if (typedChar == null) { 74 | char.classList.remove('correct_char'); 75 | char.classList.remove('incorrect_char'); 76 | 77 | // correct characters 78 | } else if (typedChar === char.innerText) { 79 | char.classList.add('correct_char'); 80 | char.classList.remove('incorrect_char'); 81 | 82 | // incorrect characters 83 | } else { 84 | char.classList.add('incorrect_char'); 85 | char.classList.remove('correct_char'); 86 | 87 | // increment number of errors 88 | errors++; 89 | } 90 | }); 91 | 92 | // display the number of errors 93 | error_text.textContent = total_errors + errors; 94 | 95 | // update accuracy text 96 | let correctCharacters = (characterTyped - (total_errors + errors)); 97 | let accuracyVal = ((correctCharacters / characterTyped) * 100); 98 | accuracy_text.textContent = Math.round(accuracyVal); 99 | 100 | // if current text is completely typed 101 | // irrespective of errors 102 | if (curr_input.length == current_quote.length) { 103 | updateQuote(); 104 | 105 | // update total errors 106 | total_errors += errors; 107 | 108 | // clear the input area 109 | input_area.value = ""; 110 | } 111 | } 112 | 113 | function updateTimer() { 114 | if (timeLeft > 0) { 115 | // decrease the current time left 116 | timeLeft--; 117 | 118 | // increase the time elapsed 119 | timeElapsed++; 120 | 121 | // update the timer text 122 | timer_text.textContent = timeLeft + "s"; 123 | } 124 | else { 125 | // finish the game 126 | finishGame(); 127 | } 128 | } 129 | 130 | function finishGame() { 131 | // stop the timer 132 | clearInterval(timer); 133 | 134 | // disable the input area 135 | input_area.disabled = true; 136 | 137 | // show finishing text 138 | quote_text.textContent = "Click on restart to start a new game."; 139 | 140 | // display restart button 141 | restart_btn.style.display = "block"; 142 | 143 | // calculate cpm and wpm 144 | cpm = Math.round(((characterTyped / timeElapsed) * 60)); 145 | wpm = Math.round((((characterTyped / 5) / timeElapsed) * 60)); 146 | 147 | // update cpm and wpm text 148 | cpm_text.textContent = cpm; 149 | wpm_text.textContent = wpm; 150 | 151 | // display the cpm and wpm 152 | cpm_group.style.display = "block"; 153 | wpm_group.style.display = "block"; 154 | } 155 | 156 | 157 | function startGame() { 158 | 159 | resetValues(); 160 | updateQuote(); 161 | 162 | // clear old and start a new timer 163 | clearInterval(timer); 164 | timer = setInterval(updateTimer, 1000); 165 | } 166 | 167 | function resetValues() { 168 | timeLeft = TIME_LIMIT; 169 | timeElapsed = 0; 170 | errors = 0; 171 | total_errors = 0; 172 | accuracy = 0; 173 | characterTyped = 0; 174 | quoteNo = 0; 175 | input_area.disabled = false; 176 | 177 | input_area.value = ""; 178 | quote_text.textContent = 'Click on the area below to start the game.'; 179 | accuracy_text.textContent = 100; 180 | timer_text.textContent = timeLeft + 's'; 181 | error_text.textContent = 0; 182 | restart_btn.style.display = "none"; 183 | cpm_group.style.display = "none"; 184 | wpm_group.style.display = "none"; 185 | } 186 | -------------------------------------------------------------------------------- /images/character_coloring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sayantanm19/js-simple-typing-game/36b86402e4367de132d3d62db4d98bbfb5243003/images/character_coloring.png -------------------------------------------------------------------------------- /images/demonstration.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sayantanm19/js-simple-typing-game/36b86402e4367de132d3d62db4d98bbfb5243003/images/demonstration.gif -------------------------------------------------------------------------------- /images/html_css_styling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sayantanm19/js-simple-typing-game/36b86402e4367de132d3d62db4d98bbfb5243003/images/html_css_styling.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Simple Speed Typer 4 | 5 | 6 | 7 |
8 |
Simple Speed Typing
9 |
10 |
11 |
WPM
12 |
100
13 |
14 |
15 |
CPM
16 |
100
17 |
18 |
19 |
Errors
20 |
0
21 |
22 |
23 |
Time
24 |
60s
25 |
26 |
27 |
% Accuracy
28 |
100
29 |
30 |
31 | 32 |
Click on the area below to start the game.
33 | 35 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fe9801; 3 | color: black; 4 | text-align: center; 5 | } 6 | 7 | .container { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | .heading { 14 | margin-bottom: 20px; 15 | font-size: 3rem; 16 | color: black; 17 | } 18 | 19 | .header { 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .timer, .errors, 25 | .accuracy, .cpm, .wpm { 26 | background-color: #ccda46; 27 | height: 60px; 28 | width: 70px; 29 | margin: 8px; 30 | padding: 12px; 31 | border-radius: 20%; 32 | box-shadow: black 5px 8px 5px; 33 | } 34 | 35 | .cpm, .wpm { 36 | display: none; 37 | } 38 | 39 | .header_text { 40 | text-transform: uppercase; 41 | font-size: 0.6rem; 42 | font-weight: 600; 43 | } 44 | 45 | .curr_time, .curr_errors, 46 | .curr_accuracy, .curr_cpm, 47 | .curr_wpm { 48 | font-size: 2.75rem; 49 | } 50 | 51 | .quote { 52 | background-color: #ccda46; 53 | font-size: 1.5rem; 54 | margin: 10px; 55 | padding: 25px; 56 | box-shadow: black 5px 8px 5px; 57 | } 58 | 59 | .input_area { 60 | background-color: #f5f5c6; 61 | height: 80px; 62 | width: 40%; 63 | font-size: 1.5rem; 64 | font-weight: 600; 65 | margin: 15px; 66 | padding: 20px; 67 | border: 0px; 68 | box-shadow: black 5px 8px 5px; 69 | } 70 | 71 | .incorrect_char { 72 | color: red; 73 | text-decoration: underline; 74 | } 75 | 76 | .correct_char { 77 | color: darkgreen; 78 | } 79 | 80 | .restart_btn { 81 | display: none; 82 | background-color: #326765; 83 | font-size: 1.5rem; 84 | padding: 10px; 85 | border: 0px; 86 | box-shadow: black 5px 8px 5px; 87 | } --------------------------------------------------------------------------------