Markdown to Anki Flashcard Converter
Convert Markdown files with Q&A pairs into Anki-compatible flashcards. Supports Obsidian_to_Anki plugin format, TSV export, cross-platform line endings.
About
Converting structured Markdown notes into Anki flashcards by hand is error-prone and slow. A misplaced delimiter or inconsistent header hierarchy silently drops cards from your import. This tool parses Markdown files that use Q:: and A:: delimiters within header-based sections (#, ##, ###), normalizes line endings across Windows (\r\n), macOS (\r), and Linux (\n), and outputs either the Pseudonium/Obsidian_to_Anki plugin format or tab-separated values for direct Anki import. It handles multi-line answers, nested code blocks, inline formatting, and generates hierarchical tags from your header structure automatically.
The original shell-script approach breaks on Windows due to carriage return handling and requires Node.js plus a specific folder structure. This browser-based converter eliminates those dependencies. It approximates the Obsidian_to_Anki plugin's START/END block syntax assuming Basic note type. Limitation: it does not process Obsidian-specific wiki-links ([[...]]) or embedded transclusions. Pro tip: keep your Q:: on a single line and let A:: span multiple lines. The parser treats everything after A:: until the next Q:: or header as the answer body.
Formulas
The parser operates in two passes. Pass 1 builds a section tree from Markdown headers. Pass 2 extracts Q&A pairs within each section.
normalize(input) → input.replace(/\r\n|\r/g, "\n")
Line ending normalization replaces all Windows (\r\n) and legacy macOS (\r) line breaks with Unix (\n) before any parsing occurs.
headerPattern = /^(#{1,6})\s+(.+)$/gm
Headers are matched by leading # characters. The count of # determines depth d where 1 ≤ d ≤ 6. Each header creates a tag segment.
tag(card) = deck ⋅ :: ⋅ join(ancestors, ::)
Tags are built hierarchically. A card under ## Chapter 1 → ### Section A generates tag deck::Chapter_1::Section_A.
questionPattern = /^Q::\s*(.+)/
answerPattern = /^A::\s*([\s\S]+?)(?=^Q::|^#{1,6}\s|\Z)/gm
Where Q:: marks the question (single line) and A:: captures everything until the next question, header, or end of file. The lazy quantifier +? prevents overmatching across sections.
Variable legend: input = raw file text. d = header depth level. deck = user-specified deck name. ancestors = array of parent header names from root to current section. card = a single Q&A pair object containing question text, answer text, tags, and source section.
Reference Data
| Feature | This Tool | Manual Copy-Paste | Node.js Script | Obsidian_to_Anki Plugin |
|---|---|---|---|---|
| Cross-platform line endings | ✓ Auto-normalized | N/A | ✗ Linux only | ✓ |
| No installation required | ✓ Browser-based | ✓ | ✗ Node.js required | ✗ Obsidian + Plugin |
| Folder structure dependency | ✗ None | ✗ None | ✓ Specific nesting | ✗ Vault structure |
| Multi-line answers | ✓ | Prone to error | Partial | ✓ |
| Code block preservation | ✓ Fenced blocks kept | Formatting lost | Partial | ✓ |
| Auto-tagging from headers | ✓ Hierarchical | ✗ | ✗ | ✓ |
| Export: Obsidian_to_Anki format | ✓ | ✗ | ✓ | Native |
| Export: Anki TSV import | ✓ | Manual | ✗ | ✗ |
| Inline Markdown rendering | ✓ Bold, italic, code, links | N/A | Partial | ✓ |
| Live card preview | ✓ | ✗ | ✗ | ✗ |
| Max file size | 10 MB | N/A | OS-dependent | Vault-dependent |
| Supported delimiters | Q:: / A:: | Any | Q:: / A:: | Configurable |
| Header depth supported | # through ###### (6 levels) | N/A | # only | Configurable |
| Wiki-link support | ✗ Plain text passthrough | N/A | ✗ | ✓ |
| Batch processing | Single file per conversion | N/A | Single file | Entire vault |
| Offline capable | ✓ Fully client-side | ✓ | ✓ | ✓ |