Technical Documentation: monkeytypegame/monkeytype
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (README.md)
About
Monkeytype is a minimalistic and customizable typing test. It features many test modes, an account system to save your typing speed history, and user-configurable features such as themes, sounds, a smooth caret, and more. Monkeytype attempts to emulate a natural typing experience during a typing test by unobtrusively presenting the text prompts and displaying typed characters in place, providing straightforward, real-time feedback on typos, speed, and accuracy.
Features
- Minimalistic design, with optional advertisements and focus mode while typing
- Type what you see, see what you type
- Live errors, wpm, and accuracy displays
- A variety of test lengths and languages
- Punctuation and numbers modes
- Quotes
- Themes
- Smooth caret
- Account system
- Challenges and just-for-fun test modifiers
- And much more
Discord bot
On the Monkeytype Discord server, we added a Discord bot to auto-assign optional roles based on typing performance and challenge completion. You can find its code over at https://github.com/monkeytypegame/monkeytype-bot.
Bug report or Feature request
If you encounter a bug or have a feature request, send us an email, create an issue, create a discussion thread, or join the Discord server.
Want to Contribute?
Refer to CONTRIBUTING.md.
Code of Conduct
Before contributing to this repository, please read the code of conduct.
Security
To report a security vulnerability, please refer to SECURITY.md.
Credits
Montydrei for the name suggestion.
Everyone who provided valuable feedback on the original Reddit post for the prototype of this website.
All of the contributors have helped implement various features, add themes, fix bugs, and more.
Support
If you wish to support further development and feel extra awesome, you can donate, become a Patron, or buy a t-shirt.
AGENT BROWSER (`docs/AGENT_BROWSER.md`)
Browser testing (agents)
Dev server
Assume the frontend dev server is already running at http://localhost:3000. Never start it (a second instance grabs port 3001) and never probe it (lsof, ps, curl โ sandboxed curl can't reach localhost anyway).
If a page load fails (ERR_CONNECTION_REFUSED, "This site can't be reached"), stop immediately and ask the user to start the server.
Tabs
New tabs open on chrome://newtab, which rejects javascript_tool and screenshots. Navigate to http://localhost:3000 first, then interact.
Before testing anything animated (page fades, anime.js, carets), check document.visibilityState. In a hidden tab Chrome pauses requestAnimationFrame, so fades never finish and pages look stuck at opacity 0. If it's hidden, ask the user to bring the Chrome window to the front.
`window.__mt` (dev only)
Defined in frontend/src/ts/dev/expose.ts; absent in production builds. Wait for it after load:
for (let i = 0; i < 30 && !window.__mt; i++) await new Promise((r) => setTimeout(r, 300));| Member | Use |
|---|---|
navigate(url) |
client-side route change |
getActivePage() |
current page name |
getConfig / setConfig(key, value) |
read/write config |
showModal(id) |
open a modal by ModalId |
restart() |
restart the typing test |
signals(filter?) |
list tracked Solid signals {name, source, value}, filter by name/source substring |
getSignal(name) / setSignal(name, value) |
read/write a tracked signal by exact name |
Read app state through these members or the DOM. Importing app modules from an injected script (import("/ts/...")) loads a separate module instance with its own state.
Older globals from frontend/src/ts/index.ts (addToGlobal) also exist in all envs: config, snapshot, qs/qsa, lastEventLog(), currentEventLog(), toggleDebugLogs().
Console
read_console_messages with a pattern โ logger output is noisy. Errors from utils/logger.ts are prefixed ERR.
CODE OF CONDUCT (`docs/CODE_OF_CONDUCT.md`)
Contributor Covenant Code of Conduct
Our Pledge
We as members, contributors, and leaders pledge to participate in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
Our Standards
Examples of behavior that contributes to a positive environment for our
community includes:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of
any kind - Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address,
without their explicit permission - Other conduct that could reasonably be considered inappropriate in a
professional setting
Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Scope
This Code of Conduct applies within all community spaces and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[email protected].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
1. Correction
Community Impact: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
Consequence: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
2. Warning
Community Impact: A violation through a single incident or series of
actions.
Consequence: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
3. Temporary Ban
Community Impact: A serious violation of community standards, including
sustained inappropriate behavior.
Consequence: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
4. Permanent Ban
Community Impact: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
Consequence: A permanent ban from any sort of public interaction within the
community.
Attribution
This Code of Conduct is adapted from the Contributor Covenant,
version 2.1, available at
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Community Impact Guidelines were inspired by
Mozilla's code of conduct enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
CONTRIBUTING (`docs/CONTRIBUTING.md`)
Contributing
**Table of Contents**
Getting Started
When contributing to Monkeytype, it's good to know our best practices, tips, and tricks. First, Monkeytype is written in JavaScript TypeScript, HTML, and CSS (in order of language usage within the project); thus, we assume you are comfortable with these languages or have basic knowledge of them. Our backend is in NodeJS and we use MongoDB to store our user data. Firebase is used for authentication. Redis is used to store ephemeral data (daily leaderboards, jobs via BullMQ, OAuth state parameters). Furthermore, we use Oxc (Oxfmt and Oxlint) to format and lint our code.
How to Contribute
We have two separate contribution guides based on what you're looking to contribute. If you're simply looking to help us augment our language or quotes data, please refer to CONTRIBUTING_BASIC.md. This guide will go over how to do so easily and without the need to set up a local development server.
If you're looking to make deeper code changes that affect functionality, or will require screenshots of the changes, please refer to CONTRIBUTING_ADVANCED.md.
Standards and Guidelines
Below is a set of general guidelines for different types of changes.
Pull Request Naming Guidelines
We use Conventional Commits for our pull request titles (and commit messages on the master branch) and also include the author name at the end inside parenthesis. Please follow the guidelines below when naming pull requests.
For types, we use the following:
feat: A new featureimpr: An improvement to an existing featurefix: A bug fixdocs: Documentation only changesstyle: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)refactor: A code change that neither fixes a bug nor adds a feature, but makes the code easier to read, understand, or improveperf: A code change that improves performancetest: Adding missing tests or correcting existing testsbuild: Changes that affect the build system or external dependencies (example scopes: vite, tsup-node, npm)ci: Changes to our CI configuration files and scripts (example scopes: GitHub Workflows)revert: Reverts a previous commitchore: Other changes that don't apply to any of the above
Examples
feat: add new feature (@github_username)impr(quotes): add english quotes (@username)fix(leaderboard): show user rank correctly (@user1, @user2, @user3)
Theme Guidelines
Before submitting a theme make sure...
- your theme is unique and isn't visually similar to any we already have.
- the text color is either black or white (or very close to these colors)
- your theme has been added to the
_listfile and thetextColorproperty is the theme's main color - your theme is clear and readable with both
flip test colorsandcolorful modeenabled and disabled
If you want to contribute themes but don't know how, check THEMES.md
Language Guidelines
- Do not include expletive words
- Ensure that your contribution meets JSON standards (no trailing comma at the end of a list)
- Be sure to add your language to the
_listand_groupsfiles - Make sure the number of words in the file corresponds to the file name (for example:
languageName.jsonis 200 words,languageName_1k.jsonis 1000 words, and so on)
If you want to contribute languages but don't know how, check LANGUAGES.md
Quote Guidelines
- Do not include content that contains any libelous or otherwise unlawful, abusive, or obscene text.
- Ensure that your contribution meets JSON standards (no trailing comma at the end of a list)
- Verify quotes added aren't duplicates of any already present
- Verify the
lengthproperty is correct (length of the text in characters) - Verify the
idproperty is incremented correctly - Please do not add extremely short quotes (less than 60 characters)
- For quotes not in English, please include translations of quotes in the description of your pull request. This assists in the verification process to ensure the integrity of the quotes.
- Remember to name your pull request properly. For example, if you are adding new quotes for the language
French, your pull request should be namedimpr(quotes): add French quotes (@yourusername).
If you want to contribute quotes but don't know how, check QUOTES.md
Layout Guidelines
If you want to contribute layouts but don't know how, check LAYOUTS.md
Questions
If you have any questions, comments, concerns, or problems let me know on GitHub, Discord in the #development channel, or ask a question on Monkeytype's GitHub discussions and a contributor will be happy to assist you.
CONTRIBUTING ADVANCED (`docs/CONTRIBUTING_ADVANCED.md`)
Contributing - Advanced
**Table of Contents**
- Contributing - Advanced
Prerequisites
This contribution guide is for cases in which you need to test the functionality of your changes, or if you need to take screenshots of your changes. You will need a computer with a stable internet connection, a text editor, Git, and NodeJS with version 24.11.0. There are some additional requirements depending on what you're looking to contribute, such as Firebase for authentication, and Mongo and Docker for the backend. Read the below sections to understand how to set up each of these tools.
Git
[!WARNING]
If you are on Windows, rungit config --global core.autocrlf falsebefore cloning this repo to prevent CRLF errors.
Git is optional but we recommend you utilize it. Monkeytype uses the Git source control management (SCM) system for its version control. Assuming you don't have experience typing commands in the command line, we suggest installing Sourcetree. You will be able to utilize the power of Git without needing to remember any cryptic commands. Using a Git client such as Sourcetree won't give you access to the full functionality of Git, but provides an easy-to-understand graphical user interface (GUI). Once you have downloaded Sourcetree, run the installer. While installing Sourcetree, keep your eyes peeled for the option to also install Git with Sourcetree. This is the option you will need to look for in order to install Git. Make sure to click yes in the installer to install Git with Sourcetree.
NodeJS and PNPM
Currently, the project is using version 24.11.0 LTS.
If you use nvm (if you use Windows, use nvm-windows) then you can run nvm install and nvm use (you might need to specify the exact version eg: nvm install 24.11.0 then nvm use 24.11.0) to use the version of Node.js in the .nvmrc file.
Alternatively, you can navigate to the NodeJS website to download it from there.
For package management, we use pnpm instead of npm or yarn. You can install it by running npm i [email protected]. This will install pnpm globally on your machine.
Docker (Recommended but Optional)
You can use docker to run the frontend and backend. This will take care of OS-specific problems but might be a bit more resource-intensive. You can download it from the Docker website.
Firebase (optional)
The account system will not let you create an account without a Firebase project. You can skip this if you don't think you will need it (you can always set it up later)
Create a Firebase account if you already haven't done so.
Create a new Firebase project.
- The project name doesn't matter, but the name
monkeytypewould be preferred. - Google Analytics is not necessary.
- The project name doesn't matter, but the name
Enable Firebase Authentication
- In the Firebase console, go to
Build > Authentication > Sign-in method - Click on
Email/Password, enable it, and save - Click on
Google, add a support email, and save
- In the Firebase console, go to
Generate a Firebase Admin private key (optional, only needed if you want to work on the backend)
- In your Firebase console, go to Project Settings > Service Accounts
- Click "Generate New Private Key"
- Save as
serviceAccountKey.jsoninside thebackend/src/credentials/directory.
Run
pnpm add firebase-toolsto install the Firebase Command Line Interface.Run
firebase loginon your terminal to log in to the same Google account you just used to create the project.Within the
frontenddirectory, duplicate.firebaserc_example, rename the new file to.firebasercand change the project name to the firebase project id you just created.- Run
firebase projects:listto find your firebase project ID. - If
.firebaserc_exampledoes not exist after cloning, create your own with:
.firebaserc{ "projects": { "default": "your-firebase-project-id" } }- Run
Config file
Within the frontend/src/ts/constants directory, duplicate firebase-config-example.ts, rename it to firebase-config.ts
- If you skipped the Firebase step, you can leave the fields blank
- Otherwise:
- Navigate to
Project Settings > General > Your apps - If there are no apps in your project, create a new web app
- In the
SDK setup and configurationsection, selectnpm - The Firebase config will be visible below
- Paste the config into
firebase-config.ts - Ensure there is an
exportstatement beforeconst firebaseConfig
- Navigate to
If you want to access the frontend from other machines on your network create a file frontend/.env with this content:
BACKEND_URL="http://<Your IP>:5005"Databases (optional if running frontend only)
Follow these steps if you want to work on anything involving the database/account system. Otherwise, you can skip this section.
Inside the backend folder, copy
example.envto.envin the same directory.- The backend Docker scripts read port bindings from this file. If
27017,6379, or5005are already in use on your machine, updateDOCKER_DB_PORT,DOCKER_REDIS_PORT, andDOCKER_SERVER_PORTbefore starting Docker.
- The backend Docker scripts read port bindings from this file. If
Setup the database server
| Manual | Docker (recommended) |
|---|---|
|
|
- (Optional) Install MongoDB-compass. This tool can be used to see and manipulate your database visually.
- To connect, type
mongodb://localhost:27017in the connection string box and press connect. The Monkeytype database will be created and shown after the server is started.
- To connect, type
Building and Running Monkeytype
It's time to run Monkeytype. Just like with the databases, you can run the frontend and backend manually or with Docker.
Dependencies (if running manually)
Run pnpm i in the project root to install all dependencies.
Both Frontend and Backend
Manual:
npm run devBackend only
| Manual | Docker |
|---|---|
npm run dev-be |
cd backend && npm run docker |
Frontend only
| Manual | Docker |
|---|---|
npm run dev-fe |
cd frontend && npm run docker |
By default, these commands will start a local development website on port 3000 and a local development server on port 5005. They will automatically rebuild the website/server when you make changes in the src/ directory. Use Ctrl+C to stop them.
[!NOTE]
Rebuilding doesn't happen instantaneously and depends on your machine, so be patient for changes to appear.
If you are on a UNIX system and you get a spawn error, run npm with sudo.
Standards and Guidelines
Code formatting and linting is enforced by Oxc (Oxfmt and Oxlint), which automatically runs every time you make a commit.
For guidelines on commit messages, adding themes, languages, or quotes, please refer to CONTRIBUTING.md. Following these guidelines will increase the chances of getting your change accepted.
Questions
If you have any questions, comments, concerns, or problems let me know on GitHub, Discord in the #development channel, or ask a question on Monkeytype's GitHub discussions and a contributor will be happy to assist you.
CONTRIBUTING BASIC (`docs/CONTRIBUTING_BASIC.md`)
Contributing - Basic
**Table of Contents**
Basic Contributions
This file details how to create basic contributions to Monkeytype purely through the use of GitHub's web UI. This means you will not need to set up a local development environment of any kind; all you'll need is a browser that can access GitHub and a GitHub account.
Given the above, you should only be using this guide if you plan on making changes that do not impact the functionality of the website. Examples of such cases would be translation fixes, language additions, or quote additions.
For all other changes, please refer to CONTRIBUTING_ADVANCED.md to learn how to set up the necessary tools to develop on your local environment.
Prerequisites
You must have a browser that can access GitHub, and possess a GitHub account. Once you have those two things, you're ready to move on to making your contribution(s)!
The steps for basic contributions are showcased splendidly in this YouTube video, so it is recommended you watch it.
Contributing
Forking Monkeytype
First, you will have to obtain your own copy of the Monkeytype repository, also known as "forking". Click here to open the fork wizard or go to the top right of your screen and then click the fork button.
This will create a clone of the repository under your own account. Navigate to your profile now and click on the new repository called monkeytype under your profile.
Making a Change
There are two methods for making a change in the code.
Option 1 - Visual Studio Code Web Editor (Recommended)
If you are not a developer and wish to contribute themes, new languages, or quotes, having a text editor will make contributions much easier. To make complex edits without installing anything, we recommend using GitHub's VS Code web editor. In your fork of Monkeytype (fork it first), go to the Code tab of the repo and press .(the period/dot key). This will open up the repo in an online VS Code instance you can use to edit files in the browser. Once you are done making your changes, go to the Source Control tab in the activity bar with Ctrl/Cmd + Shift + G, click the + next to the files you've changed to stage them, type a brief message summarizing the changes made in the commit, and press Ctrl/Cmd + Enter to commit your changes to your fork.
Once done, move on to the next section to create a pull request.
Option 2 - GitHub Web UI
You're now ready to make a change. Navigate to the file that you're looking to contribute to in your forked repository. Once you navigate to the file, you should see an Edit icon (shaped like a pencil) on the right:
Upon clicking this, you'll have the ability to edit the document itself.
Note however that some files that are too large might not have this option. In these cases, you will need to download the code and create edits outside of the GitHub web UI. Refer to CONTRIBUTING_ADVANCED.md
At this point, you should take a look at CONTRIBUTING.md to view guidelines for theme, language, and quote contributions.
Once you've completed your change, you're ready to commit it. At the bottom of the edit file screen, you will find the commit UI. In the first box, you want to put in a title that describes the change you made. Then in the description field, you can put in any additional detail to supplement your title further.
You will find two radio buttons, one prompts you to commit directly to your current branch, and the other prompts you to create a new branch for your commit and start a pull request. Select the first option to commit the change directly to your current branch.
Click Commit changes once you are ready to proceed.
Creating a Pull Request
You can repeat the steps above for as many changes as needed. Once you are done making all your code changes and you have committed them to your branch, you are ready to make a pull request (PR). Go back to the main page of your forked repository. Ensure that your current branch (which is likely still master at this point) is up to date. You can do so by clicking the following button:
Update branch:
Once up to date, you can click the Contribute button to open a PR.
Create a pull request:
Be sure to add a good description to the PR and that the source and destination branches at the top look correct. The base repository and base branch should be listed as monkeytypegame/monkeytype and master respectively, and on the right of that should be your forked repository and the branch you have your changes on.
Once done, click on Create pull request to officially publish your PR.
Merging a Pull Request
All you have to do now is wait for approval or comments and go from there!
Once your PR is approved, all that is left to do is merge it!
Questions
If you have any questions, comments, concerns, or problems, don't hesitate to let us know via email(to Miodec), on GitHub or on Discord in the #development channel and a contributor will be happy to assist you.
FONTS (`docs/FONTS.md`)
**Table of Contents**
Forking Monkeytype
First, you will have to make a personal copy of the Monkeytype repository, also known as "forking". Go to the Monkeytype repo and then click the "fork" button.
Adding Fonts
Once you have forked the repository you can now add your font. Place the font file in ./frontend/static/webfonts e.g. My-Font.woff2.
[!NOTE]
Your font needs to be in the.woff2format. Your filename cannot include spaces or start with a number.
Open ./packages/schemas/src/fonts.ts and add the new font at the end of the KnownFontNameSchema list like this:
const KnownFontNameSchema = z.enum(
[
"Roboto_Mono",
"Noto_Naskh_Arabic",
...
"My_Font",Call it whatever you want but make sure you replace spaces with underscores and the font does not start with a number.
Then, go to ./frontend/src/ts/constants/fonts.ts and add the following code to the end of the Fonts object near to the very end of the file:
export const Fonts: Record<KnownFontName, FontConfig> = {
...
My_Font: {
fileName: "My-Font.woff2",
}Committing Languages
Once you have created your language, you now need to create a pull request to the main Monkeytype repository. Go to the branch where you created your languages on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
Update branch:
Create a pull request:
Language Guidelines
Make sure your language follows the Language guidelines.
LANGUAGES (`docs/LANGUAGES.md`)
**Table of Contents**
Forking Monkeytype
First, you will have to make a personal copy of the Monkeytype repository, also known as "forking". Go to the Monkeytype repo and then click the "fork" button.
Creating Languages
Once you have forked the repository you can now add your language. Create a new JSON file in ./frontend/static/languages/, named as the language name and the number of words, e.g. language_1k.json. If there are less than 1,000 words, simply name the file after the language (e.g. language.json). Note that a minimum of 200 words are required.
The contents of the file should be as follows:
{
"name": string,
"rightToLeft": boolean,
"joiningScript": boolean,
"orderedByFrequency": boolean,
"bcp47": string,
"preferredFont":string,
"words": string[]
}It is recommended that you familiarize yourselves with JSON before adding a language. For the name field, put the name of your language.rightToLeft indicates how the language is written. If it is written right to left then put true, otherwise put false.joiningScript indicates whether the language requires joining letters to render correctly. Set it to true if characters must join with surrounding characters or if their shapes change based on position in a word (initial, medial, final, or isolated), or if they use connecting marks (matras/vowel signs) that reshape the base characters. Otherwise, set it to false.
For bcp47 put your languages IETF language tag.
If the words you're adding are ordered by frequency (most common words at the top, least at the bottom) set the value of orderedByFrequency to true, otherwise false.
Finally, add your list of words to the words field.
If the language is not rendered correctly, you can provide a preferredFont. The font needs to be one of monkeytypes fonts. Check the fonts documentation.
Then, go to packages/schemas/src/languages.ts and add your new language name at the end of the LanguageSchema enum. Make sure to end the line with a comma. Make sure to add all your language names if you have created multiple word lists of differing lengths in the same language.
export const LanguageSchema = z.enum([
"english",
"english_1k",
..."your_language_name",
"your_language_name_10k",
]);Then, go to frontend/src/ts/constants/language.ts and add your new language name to the LanguageGroups map. You can either add it to an existing group or add a new one. Make sure to add all your language names if you have created multiple word lists of differing lengths in the same language.
export const LanguageGroups: Record<string, Language[]> = {
...
your_language_name: [
"your_language_name",
"your_language_name_10k",
]
};Committing Languages
Once you have created your language, you now need to create a pull request to the main Monkeytype repository. Go to the branch where you created your languages on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
Update branch:
Create a pull request:
Language Guidelines
Make sure your language follows the Language guidelines.
LAYOUTS (`docs/LAYOUTS.md`)
**Table of Contents**
Forking Monkeytype
First, you will have to make a personal copy of the Monkeytype repository, also known as "forking". Go to the Monkeytype repo and then click the "fork" button.
Creating Layouts
Once you have forked the repository you can now add your layout. Create a new JSON file in ./frontend/static/layouts/, named as the layout name, e.g. qwerty.json.
The contents of the file should be as follows:
{
"keymapShowTopRow": false,
"type": "ansi",
"keys": {
"row1": [
["`", "~"],
["1", "!"],
["2", "@"],
["3", "#"],
["4", "$"],
["5", "%"],
["6", "^"],
["7", "&"],
["8", "*"],
["9", "("],
["0", ")"],
["-", "_"],
["=", "+"]
],
"row2": [
["q", "Q"],
["w", "W"],
["e", "E"],
["r", "R"],
["t", "T"],
["y", "Y"],
["u", "U"],
["i", "I"],
["o", "O"],
["p", "P"],
["[", "{"],
["]", "}"],
["\\", "|"]
],
"row3": [
["a", "A"],
["s", "S"],
["d", "D"],
["f", "F"],
["g", "G"],
["h", "H"],
["j", "J"],
["k", "K"],
["l", "L"],
[";", ":"],
["'", "\""]
],
"row4": [
["z", "Z"],
["x", "X"],
["c", "C"],
["v", "V"],
["b", "B"],
["n", "N"],
["m", "M"],
[",", "<"],
[".", ">"],
["/", "?"]
],
"row5": [[" "]]
}
}It is recommended that you familiarize yourselves with JSON before adding a layout.
keymapShowTopRow indicates whether to always show the first row of the layout.type can be ansi or iso.
In keys you need to specify row1 to row5. Add the keys within the row as string-array. The string-array can have up to four character. The character define unshifted, shifted, alt-gr and shifted alt-gr character in this order. For example ["e","E","โฌ"] defines e on regular key press, E if shift is held and โฌ if alt-gr is held.
Note: Quote and backslash characters need to be escaped: \" and \\.
For ansi layouts the number of keys need to be exactly thirteen for row1 and row2, eleven for row3, ten for row4 and one or two for row5.
For iso the number of keys need to be exactly thirteen for row1, twelve for row2 and row3, eleven for row4 and one or two for row5.
In addition to the layout file you need to add your layout to the packages/schemas/src/layouts.ts file. Just append your layout name (without the .json) at the end of the LayoutNameSchema. Remember to add a comma like this:
export const LayoutNameSchema = z.enum([
"qwerty",
"dvorak",
"colemak",
..."your_layout_name",
]);Committing Layouts
Once you have created your layout, you now need to create a pull request to the main Monkeytype repository. Go to the branch where you created your layout on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
Update branch:
Create a pull request:
Make sure your PR title follow the syntax feat(layout): add <YOUR_LAYOUT> layout (@<YOUR_GITHUB_NAME>), e.g. feat(layout): add qwerty layout (@teddinotteddy)
Layout Guidelines
Make sure your layout follows the Layout guidelines.
QUOTES (`docs/QUOTES.md`)
**Table of Contents**
Forking Monkeytype
First you will have to copy the Monkeytype repository also known as forking. Go to the Monkeytype Repo and then click the "fork" button.
Creating Quotes
After you forked the Monkeytype repository you can now add your quotes. (If you haven't already forked the repository, refer to this section.) (Before continuing to the next step make sure the quote's language exists in Monkeytype) Add this code in at the end of the quotes ./frontend/static/quotes/[language].json:
{
"text": "[quote]",
"source": "[source]",
"id": [number of the quote],
"length": [number of characters in quote]
}If the language does exist in Monkeytype, but there are no quotes for it create a new file for the language.
Committing Quotes
Once you have added your quote(s), you now need to create a pull request to the main Monkeytype repository. Go to the branch where you added your quotes on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
Update branch:
Create a pull request:
Quote Guidelines
Make sure your quote(s) follows the Quote guidelines.
SECURITY (`docs/SECURITY.md`)
Security Policy
We take the security and integrity of Monkeytype very seriously. If you have found a vulnerability, please report it ASAP so we can quickly remediate the issue.
Reporting a Vulnerability
For vulnerabilities that impact the confidentiality, integrity, and availability of Monkeytype services, please send your disclosure via email. For non-security related platform bugs, follow the bug submission guidelines. Include as much detail as possible to ensure reproducibility. At a minimum, vulnerability disclosures should include:
- Vulnerability Description
- Proof of Concept
- Impact
- Screenshots or Proof
Submission Guidelines
Do not engage in activities that might cause a denial of service condition, create significant strains on critical resources, or negatively impact users of the site outside of test accounts.
SELF HOSTING (`docs/SELF_HOSTING.md`)
Monkeytype Self Hosting
Table of contents
Prerequisites
- you need to have
dockeranddocker-compose-plugininstalled. Follow the docker documentation on how to do this.
Quickstart
- create a new directory (e.g.
monkeytype) and navigate into it. - download the docker-compose.yml file.
- create an
.envfile, you can copy the content from the example.env. - download the backend-configuration.json
- run
docker compose up -d - after the command exits successfully you can access http://localhost
Hosting over the network (HTTPS)
If you plan to access your self-hosted Monkeytype instance over a local network or the internet (not using localhost), you must serve it over HTTPS. Modern browsers restrict key web features, such as crypto.randomUUID, to secure contexts. Accessing the site via HTTP over a network will cause the frontend to crash with errors like Uncaught TypeError: crypto.randomUUID is not a function.
Enable HTTPS
Update the .env file and uncomment these lines and set the values based on your domain.
DOMAIN=mydomain.com
BASE_URL=https://mydomain.com
[email protected]Update the docker-compose.yml and uncomment all lines marked with # enable for HTTPS.
Troubleshooting Frontend Connection Issues
If your reverse proxy is up but you see errors like Looks like the server is experiencing unexpected down time or network errors when fetching resources, your frontend is likely trying to communicate with the backend over unsecure HTTP, causing a Mixed Content block in the browser.
Ensure you configure the frontend to talk to your secure backend URL by following these rules in your .env file:
- Update the frontend and backend URL: Set
DOMAINandBASE_URLcorrectly, usuallyBASE_URLishttps://DOMAIN. - Do not include a trailing slash: Ensure the URL does not end with a
/(e.g., usehttps://yourdomain.com, nothttps://yourdomain.com/). A trailing slash will cause404 Not Founderrors due to double slashes in the API calls (like//configuration). - Force container recreation: Monkeytype is a Single Page Application (SPA), meaning environment variables are baked into the static JavaScript files during startup. If you change your
.env, you must completely recreate the container for the changes to apply:
docker compose up -d --force-recreate[!TIP]
After updating your configuration and recreating the containers, clear your browser cache or perform a hard reload (Ctrl + F5) to make sure your browser isn't running an old cached version of the frontend.
Security
Do not expose the Monkeytype backend directly to the internet. Instead, place it behind a reverse proxy and configure the backend to only accept connections from the reverse proxy.
The backend's built-in rate limiting is based on the authenticated user's uid or, for unauthenticated requests, the client's IP address.
To determine the client's IP address, the backend checks the following sources in order:
CF-Connecting-IP(when requests are proxied through Cloudflare)X-Forwarded-For- The source IP address of the HTTP connection
We recommend the following configuration:
- If you are not using Cloudflare, remove any incoming
CF-Connecting-IPheader in your reverse proxy before forwarding requests. - Configure your reverse proxy to set the
X-Forwarded-Forheader to the client's IP address. - Configure the backend to only accept connections from the reverse proxy to prevent clients from spoofing trusted headers.
Sources:
Account System
By default, user sign-up and login are disabled. To enable this, you'll need to set up a Firebase project.
Stop the running docker containers using docker compose down before making any changes.
Setup Firebase
create a Firebase account
create a new Firebase project.
- name "monkeytype"
- uncheck "enable google analytics"
enable authentication
- open the firebase console and open your project
- go to
Authentication > Sign-in method - enable
Email/Passwordand save
whitelist your domain
- In the Firebase console, go to
Authentication > Sign-in method - Scroll to
Authorized domains - Click
Add domainand enter the domain where youโll host the Monkeytype frontend (e.g.localhost)
- In the Firebase console, go to
generate service account
- go to your project settings by clicking the
โicon in the sidebar, thenProject settings - navigate to the
Service accountstab - click
Generate new private keyto download the.jsonfile. - save it as
serviceAccountKey.json - update
docker-compose.ymland uncomment the volume block in themonkeytype-backendcontainer to mount the Firebase service account:yaml#uncomment to enable the account system, check the SELF_HOSTING.md file - type: bind source: ./serviceAccountKey.json target: /app/backend/src/credentials/serviceAccountKey.json read_only: true
- go to your project settings by clicking the
update the
.envfile- open the firebase console and open your project
- open the project settings by clicking the
โicon on the sidebar andProject settings - if your project has no apps yet, create a new Web app (
</>icon)- nickname
monkeytype - uncheck
set up firebase hosting - click
Register app
- nickname
- select your app and select
ConfigforSDK setup and configuration - it will display something like this:text
const firebaseConfig = { apiKey: "AAAAAAAA", authDomain: "monkeytype-00000.firebaseapp.com", projectId: "monkeytype-00000", storageBucket: "monkeytype-00000.appspot.com", messagingSenderId: "90000000000", appId: "1:90000000000:web:000000000000" }; - update the
.envfile with the values above:textFIREBASE_APIKEY=AAAAAAAA FIREBASE_AUTHDOMAIN=monkeytype-00000.firebaseapp.com FIREBASE_PROJECTID=monkeytype-00000 FIREBASE_STORAGEBUCKET=monkeytype-00000.appspot.com FIREBASE_MESSAGINGSENDERID=90000000000 FIREBASE_APPID=1:90000000000:web:000000000000
Update backend configuration
- update the
backend-configuration.jsonfile and add/modifyjson{ "users": { "signUp": true, "profiles": { "enabled": true } } }
Setup Recaptcha
- create a new recaptcha token
- label:
monkeytype - type: v2
- domain: the domain of the frontend
- label:
- update the
.envfile with the site key from the previous steptextRECAPTCHA_SITE_KEY="your site key" RECAPTCHA_SECRET="your secret key"
If you host privately you can use these defaults:
RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
RECAPTCHA_SECRET=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWeSetup email (optional)
To enable emails for password reset and email verification update the following config in .env file:
# email server config
# uncomment below if you want to send emails for e.g. password reset
EMAIL_HOST=mail.myserver # your mailserver domain
EMAIL_USER=mailuser # username to authenticate with your mailserver
EMAIL_PASS=mailpass # password for the user
EMAIL_PORT=465 # port, likely 465 or 587
EMAIL_FROM="Support <noreply@myserver>"Enable daily leaderboards
To enable daily leaderboards update the backend-configuration.json file and add/modify
{
"dailyLeaderboards": {
"enabled": true,
"maxResults": 250,
"leaderboardExpirationTimeInDays": 1,
"validModeRules": [
{
"language": "english",
"mode": "time",
"mode2": "15"
},
{
"language": "english",
"mode": "time",
"mode2": "60"
}
]
}
}- language is one of the supported language
- mode can be
timeorwords - mode2 can be
15,30,60or120if you pickedmode=timeor10,25,50or100if you pickedmode=words.
Configuration files
env file
All settings are described in the example.env file.
serviceAccountKey.json
Contains your firebase config, only needed if you want to allow users to signup.
backend-configuration.json
Configuration of the backend. Check the default configuration for possible values.
[!NOTE]
Configuration changes are applied only on container startup. You must restart the container for your updates to take effect.
THEMES (`docs/THEMES.md`)
**Table of Contents**
Forking Monkeytype
First you will have to make a personal copy of the Monkeytype repository, also known as "forking". Go to the Monkeytype repo and then click the "fork" button.
Creating Themes
Pick a name for your theme. It must be all lowercase, with spaces replaced by underscores.
Go to ./packages/schemas/src/themes.ts and add your new theme name to the end of the ThemeNameSchema enum. Make sure to end the line with a comma.
export const ThemeNameSchema = z.enum([
"8008",
"80s_after_dark",
... all existing theme names
"your_theme_name",
]);Then, go to ./frontend/src/ts/constants/themes.ts and add the following code to the end of the themes object near to the very end of the file:
export const themes: Record<ThemeName, Theme> = {
... all existing themes
your_theme_name: {
bg: "#ffffff",
caret: "#ffffff",
main: "#ffffff",
sub: "#ffffff",
subAlt: "#ffffff",
text: "#ffffff",
error: "#ffffff",
errorExtra: "#ffffff",
colorfulError: "#ffffff",
colorfulErrorExtra: "#ffffff",
},
}Here is an image showing what all the properties correspond to:
If you don't want to add any custom styling you can skip the next section.
Adding custom CSS (optional)
Create a CSS file in ./frontend/static/themes/ matching the name you picked earlier. Update the theme configuration in ./frontend/src/ts/constants/themes.ts and add hasCss: true like this:
export const themes: Record<ThemeName, Theme> = {
... all existing themes
your_theme_name: {
bg: "#ffffff",
caret: "#ffffff",
main: "#ffffff",
sub: "#ffffff",
subAlt: "#ffffff",
text: "#ffffff",
error: "#ffffff",
errorExtra: "#ffffff",
colorfulError: "#ffffff",
colorfulErrorExtra: "#ffffff",
hasCss: true,
},
}Committing Themes
Once you have created your theme(s), you now need to create a pull request to the main Monkeytype repository. Go to the branch where you created your new theme on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
Update branch:
Create a pull request:
Add some screenshots of your theme to the pull request. Click "create pull request" and if it gets approved then your new theme is available on Monkeytype for everyone to enjoy.
Theme Guidelines
Make sure your theme follows the Theme guidelines.
--- METRICS ---
- Files Extracted: 13
- Estimated Token Budget: ~15185 tokens
- Canonical Reference: https://codewiki.google/github.com/monkeytypegame/monkeytype