Get Started

Talk into your phone, get a tidy to-do list on your PC

Record a thought on your phone. A few minutes later it's a titled, tagged note on your PC with a 3-line summary and a to-do list, made by Whisper and a local AI model. No cloud service, no subscription, and your voice never leaves your own devices.

75 minIntermediateTools: FreeStep 1 free
A Python script that watches an inbox folder and turns every new recording into a Markdown note
Each note gets a title, tags, a 3-bullet summary and tick-box action items, written by a local model and checked against a schema
Phone memos arriving by themselves through Syncthing, and the script running every 15 minutes in the background
  • Notes that open in Obsidian, import into Joplin, and search from the terminal ("show me every open to-do")
  • The full project as a starter kit zip, with a dry run you can try before downloading any models
Before you start
  • Done first: Run AI on your PC (Ollama 0.20.7 or newer with gemma4:e2b) and Subtitles for any video (it installs uv and the Whisper model this reuses)
  • For the phone part: Your own private Dropbox (Syncthing), plus Syncthing on your phone
  • 8GB of RAM minimum, 16GB is more comfortable. Whisper and the model take turns, so they don't both need memory at once
  • About 6GB of free disk: roughly 250MB of Python packages, the 465MB Whisper model and the 4.6GB Gemma model (less if you already have them)
  • Optional: Joplin or Obsidian to read the notes. Both are free
  • No accounts, no API keys, nothing to pay
01/07Get the kit and do a dry run, no models needed0 done
STEP 01 of 07~10 min

Get the kit and do a dry run, no models needed

Prove the folder handling, JSON checks and note writing work on your machine before you download anything big.

  1. Diagram: phone recording goes through Syncthing to an inbox folder, then Whisper, then Ollama, then a Markdown note, with the audio moved to a done folder last
    What you're building. Each box is a few lines of one Python script.
  2. Download the starter kit (14KB) and unzip it into your home folder. It contains a folder called voice-notes with four files: the main script voice_notes.py, its settings in settings.toml, a search tool search_notes.py, and selftest.py, a dry run.

    Windows
    cd $HOME\Downloads
    Expand-Archive voice-notes-kit.zip -DestinationPath $HOME
    cd $HOME\voice-notes
    Mac / Linux
    cd ~/Downloads
    unzip voice-notes-kit.zip -d ~
    cd ~/voice-notes
  3. Each script starts with a small # /// script block listing the Python packages it needs. That's a standard format (PEP 723) that uv reads: uv run builds a private, cached environment with exactly those packages, so there's no pip install and nothing touches your system Python. You installed uv in the subtitles lesson.

  4. The dry run swaps Whisper and Ollama for fakes, then throws awkward cases at the real pipeline: a model reply with the wrong number of bullets, a reply that isn't JSON at all, an audio file that can't be decoded, Syncthing's half-written temp files, a text file, a file that's still being copied, and the same recording arriving twice.

    Terminal
    uv run selftest.py
  5. It takes a few seconds and prints this (your dates and times will differ):

    Output
    2026-10-09 15:53:33 INFO Still arriving, will try next run: memo-07-still-copying.wav
    2026-10-09 15:53:33 INFO 4 recording(s) to process
    2026-10-09 15:53:33 INFO Wrote 2026-10-09-1553-saturday-errands-mot-dentist-bin-bags.md (errands, car, health-stuff)
    2026-10-09 15:53:33 WARNING Model reply failed validation (attempt 1): summary: List should have at least 3 items after validation, not 2
    2026-10-09 15:53:33 INFO Wrote 2026-10-09-1553-coffee-loyalty-card-idea.md (shop, ideas)
    2026-10-09 15:53:33 WARNING Model reply failed validation (attempt 1): json: Invalid JSON: expected value at line 1 column 1
    2026-10-09 15:53:33 WARNING Model reply failed validation (attempt 2): json: Invalid JSON: expected value at line 1 column 1
    2026-10-09 15:53:33 INFO Wrote 2026-10-09-1553-voice-note-memo-03.md (needs-review)
    2026-10-09 15:53:33 ERROR Failed on memo-04-broken.wav (try 1 of 3): Invalid data found when processing input
    2026-10-09 15:53:35 INFO 2 recording(s) to process
    2026-10-09 15:53:35 INFO Already made a note from memo-01-copy.wav, moved it to done/2026-10/memo-01-copy.wav
    2026-10-09 15:53:35 ERROR Failed on memo-04-broken.wav (try 2 of 3): Invalid data found when processing input
    2026-10-09 15:53:36 INFO 1 recording(s) to process
    2026-10-09 15:53:36 ERROR Failed on memo-04-broken.wav (try 3 of 3): Invalid data found when processing input
    2026-10-09 15:53:36 ERROR Gave up on memo-04-broken.wav, moved it to the failed folder
    == Run 1 ==
    {'ok': 3, 'duplicate': 0, 'error': 1, 'failed': 0}
    
    == Run 2: the same recording arrives again under a new name ==
    {'ok': 0, 'duplicate': 1, 'error': 1, 'failed': 0}
    
    == Run 3 ==
    {'ok': 0, 'duplicate': 0, 'error': 1, 'failed': 1}
    
    == Folders ==
    inbox/: ['.syncthing.memo-05.wav.tmp', 'shopping.txt', '~syncthing~memo-06.wav.tmp']
    notes/: ['2026-10-09-1553-coffee-loyalty-card-idea.md', '2026-10-09-1553-saturday-errands-mot-dentist-bin-bags.md', '2026-10-09-1553-voice-note-memo-03.md']
    done/: ['2026-10/memo-01-copy.wav', '2026-10/memo-01.wav', '2026-10/memo-02.wav', '2026-10/memo-03.wav']
    failed/: ['memo-04-broken.wav', 'memo-04-broken.wav.error.txt']
    
    == 2026-10-09-1553-saturday-errands-mot-dentist-bin-bags.md ==
    ---
    title: "Saturday errands: MOT, dentist, bin bags"
    created: 2026-10-09T15:53:32
    updated: 2026-10-09T15:53:33
    tags:
      - errands
      - car
      - health-stuff
    audio: "memo-01.wav"
    audio_sha256: c4a948f325d39c925685c2f91f30453f46903bf6e1826eed691cbd8f86643061
    language: en
    duration_seconds: 12
    ---
    
    ## Summary
    
    - The Fiesta's MOT is due before the 20th.
    - Tuesday's dentist appointment needs moving.
    - Bin bags are needed.
    
    ## Action items
    
    - [ ] Book the MOT for the Fiesta before the 20th
    - [ ] Ring the dentist to move Tuesday's appointment
    - [ ] Buy bin bags
    
    ## Transcript
    
    Right, reminder for Saturday. I need to book the MOT for the Fiesta before the 20th, and ring the dentist to move Tuesday's appointment. Also pick up bin bags.
    
    *From `memo-01.wav`, 12 seconds, transcribed on this computer.*
    
    No BOM at the start of the note: OK
    • Run 1: three notes written. The second one only after a retry (the fake model sent 2 summary bullets, validation caught it, the script explained the error and asked again). The third got a safe fallback note tagged needs-review, because its reply was never valid JSON. The file still being copied was left alone.
    • The last lines print the finished note and confirm it starts cleanly, with no hidden byte order mark that would confuse Joplin.
    • Run 2: the same audio under a new name was recognised by its fingerprint and filed without making a second note.
    • Run 3: the undecodable file failed for the third time and moved to failed, with an .error.txt saying why.
    • The inbox still holds the Syncthing temp files and the text file, untouched.
  6. Delete the test folder when you've had a look: Remove-Item -Recurse selftest-run on Windows, or rm -r selftest-run on Mac and Linux.

✓
Checkpointuv run selftest.py prints {'ok': 3, ...} for Run 1 and ends with No BOM at the start of the note: OK.
If it breaks'uv' is not recognised + 2 more
'uv' is not recognised
Install it with the one-liner from docs.astral.sh/uv ↗, then close the terminal and open a new one.
"No such file or directory: selftest.py"
You're in the wrong folder. Unzipping by double-click often makes voice-notes-kit\voice-notes. Run Get-ChildItem (or ls) and cd into the folder that contains selftest.py.
"No interpreter found for Python >=3.11"
uv normally downloads Python by itself. If your network blocks that, run uv python install 3.12 once and try again.
Still stuck? Ask the community
Next mission · Own your AIRent a cloud GPU by the hour to run models your laptop can't
→