An open-source EASA ATPL question bank and practice platform. No accounts, no subscriptions, no fees — just questions. All progress is saved locally in your browser.
- Subject-based question bank covering EASA 2020 ECQB subjects
- Test creation with subject selection and question count slider
- Interactive test runner with instant answer feedback
- Keyboard shortcuts — arrow keys to navigate, A–D to answer, F to flag
- Question flagging — mark questions for review (persists across sessions)
- Personal notes — add notes per question (500 char limit)
- Progress tracking — elapsed time, correct/incorrect rate, completion status
- Finish test — explicit finish action with pass/fail result (75% threshold)
- Dark mode — toggle in the header, respects system preference
- Attachment previews — question images displayed inline
- Fully client-side — no backend required
- Node.js 18+
- npm
git clone https://github.com/oliks/openatpl.git
cd openatpl
npm install
npm run devOpen http://localhost:3000.
The platform is manifest-driven. All question data lives in data/tests/.
data/tests/
manifest.json # Registry of all subjects
010/
index.json # Subject metadata + question list
questions/
0001.json # Individual question file
0002.json
...
public/attachments/
010/
010-att-0001.jpg # Attachment images ({subjectCode}-att-{number})
An array of subject entries:
[
{
"id": "032",
"name": "032 - Performance",
"subject": "Performance",
"description": "ATPL subject bank.",
"sourceFile": "data/tests/032/index.json"
}
]| Field | Description |
|---|---|
id |
Unique subject code (e.g. "032") |
name |
Display name shown in the UI |
subject |
Subject category |
description |
Short description |
sourceFile |
Path to the subject's index.json |
Each subject has an index.json that lists all questions:
{
"subjectCode": "032",
"subjectName": "Performance",
"questionCount": 1356,
"questions": [
{
"number": 1,
"file": "questions/0001.json",
"questionId": "032-0001",
"questionNumber": "032-0001",
"correctOption": "a",
"hasAttachment": true
}
]
}Each question is a standalone JSON file:
{
"number": 1,
"questionId": "032-0001",
"questionNumber": "032-0001",
"stemHtml": "<p>What is the minimum TOD?</p>",
"options": {
"a": "<p>2860 ft</p>",
"b": "<p>3300 ft</p>",
"c": "<p>3220 ft</p>",
"d": "<p>2200 ft</p>"
},
"correctOption": "a",
"attachments": [
{
"uniqueKey": "032-att-0001",
"publicUrl": "/attachments/032/032-att-0001.jpg"
}
]
}| Field | Required | Description |
|---|---|---|
number |
Yes | Sequential number within the subject |
questionId |
Yes | Unique identifier in format {subjectCode}-{number} (e.g. "032-0001") |
questionNumber |
Yes | Same as questionId |
stemHtml |
Yes | Question text (HTML) |
options |
Yes | Answer choices keyed a through f (HTML) |
correctOption |
Yes | Letter of the correct answer or null if unknown |
attachments |
No | Array of image attachments |
| Field | Description |
|---|---|
uniqueKey |
Attachment identifier (e.g. 032-att-0001) |
publicUrl |
Path relative to public/ (e.g. /attachments/032/032-att-0001.jpg) |
Place image files in public/attachments/<subjectCode>/.
Contributions are welcome! Whether you're adding new subjects, fixing incorrect answers, or improving the platform — here's how.
# 1. Fork this repo on GitHub (click the Fork button)
# 2. Clone your fork
git clone https://github.com/<your-username>/openatpl.git
cd openatpl
# 3. Add upstream remote
git remote add upstream https://github.com/oliks/openatpl.git-
Create the folder structure:
data/tests/<subjectCode>/ index.json questions/ 0001.json 0002.json ... -
Add an entry to
data/tests/manifest.json:{ "id": "<subjectCode>", "name": "<subjectCode> - <Subject Name>", "subject": "<Subject Name>", "description": "ATPL subject bank.", "sourceFile": "data/tests/<subjectCode>/index.json" } -
Create
index.jsonlisting all questions withnumber,file,questionId,questionNumber, andcorrectOption. -
Create individual question JSON files following the format above.
-
If questions have image attachments, place them in
public/attachments/<subjectCode>/and reference them in the question'sattachmentsarray.
-
Add the question JSON file to
data/tests/<subjectCode>/questions/with the next sequential number (e.g.0457.json). -
Add a corresponding entry to the subject's
index.jsonquestionsarray. -
Update the
questionCountinindex.json.
# 1. Create a feature branch
git checkout -b add-<subjectCode>-questions
# 2. Add your files
git add data/tests/<subjectCode>/ data/tests/manifest.json
git add public/attachments/<subjectCode>/ # if adding images
# 3. Commit
git commit -m "Add questions for <Subject Name>"
# 4. Push to your fork
git push origin add-<subjectCode>-questions
# 5. Open a Pull Request on GitHub against mainEvery pull request runs automated checks:
| Check | What it does |
|---|---|
| Lint | Runs next lint to catch code issues |
| Build | Ensures the project builds successfully |
| Validate Question Bank | Verifies manifest structure, checks that all referenced index.json and question files exist |
All checks must pass before a PR can be merged. If the validate step fails, check that:
- Your manifest entry has
id,name, andsourceFile - The
sourceFilepath points to a validindex.json - All questions listed in
index.jsonhave corresponding files in thequestions/folder
- Follow the existing JSON format exactly — the validator will catch structural issues
- Use sequential numbering for question files (
0001.json,0002.json, ...) - Keep
correctOptionaccurate — set tonullif unsure rather than guessing - Crop any watermarks from attachment images before submitting
- One subject per PR keeps reviews manageable
app/ # Next.js App Router pages
layout.jsx # Root layout (header, footer, theme)
page.jsx # Home (saved tests list)
create-test/page.jsx # Test creation form
tests/[testId]/run/page.jsx # Test runner
globals.css # All styles (CSS variables, dark mode)
components/
MyTestsList.jsx # Saved tests grid
CreateTestForm.jsx # Subject + question count form
TestRunner.jsx # Interactive quiz engine
ThemeToggle.jsx # Dark/light mode toggle
lib/
test-bank.js # Server-side test data loader
saved-tests-client.js # Client-side localStorage management
derive-test-client.js # Question filtering for derived tests
data/tests/ # Question bank data
public/attachments/ # Question images
.github/workflows/ci.yml # PR checks (lint, build, validate)
Questions in this repository are contributed by the community based on publicly available EASA ATPL study material. OpenATPL is not affiliated with, endorsed by, or connected to EASA, any national aviation authority, or any commercial question bank provider.
This project is intended solely as a free study aid for student pilots. The questions are reconstructed from memory by exam candidates and may not reflect the exact wording or content of any official examination.
If you believe any content in this repository infringes on your intellectual property rights, please open a GitHub issue or contact us directly. We take all claims seriously and will promptly review and remove any content that is found to be infringing.
Please include in your request:
- Identification of the copyrighted work you believe is being infringed
- The specific files or content you want removed
- Your contact information
- A statement that you have a good faith belief that the use is not authorised
We aim to respond to all valid requests within 48 hours.
OpenATPL is free and open source. If you find it useful, consider supporting development — it helps cover hosting, domains, and ongoing maintenance.
OpenATPL is open source. No warranties or liabilities are provided. Use at your own risk.
