Every model runs on your machine

It joins the call.
Nothing leaves the room.

Send a meeting link to a Telegram bot. Tekki shows up in the Meet as its own participant, listens, thinks about it, and answers out loud with a face that reacts. Ollama does the thinking, whisper the listening, Kokoro the voice — all of it local.

Free & open sourceNothing leaves the machineMac or Linux

meet.google.com/xyz-abcd-efg
Tekkibot
JMJules
ARAri
room

…so are we shipping the migration this week?

try an expression:
  • Playwright
  • Ollama
  • whisper.cpp
  • Kokoro · mlx-audio
  • PulseAudio
  • Canvas2D
  • Docker
  • Telegram

How it gets in

Meet has no plugin API.
So Tekki becomes the hardware.

To be in a video call you need to be two devices: a camera and a microphone. Everything else is detail. The AI half is commodity — the device half is where the work is.

  1. 01hear

    PulseAudio null sink

    Chrome plays the meeting into a sink that isn't a speaker. One parec records its monitor — every participant mixed for free.

  2. 02hear

    whisper.cpp

    The monitor stream becomes text on the host, where it keeps its GPU acceleration. Base English model, no timestamps.

  3. 03think
    [thinking]“Only if…”

    Ollama

    A small non-reasoning model answers in a third of a second and tags its own expression inline, e.g. [thinking].

  4. 04speak

    Kokoro

    The reply becomes PCM through mlx-audio — a voice that doesn't sound like a satnav — and its amplitude drives the mouth.

  5. 05show

    canvas.captureStream()

    The avatar is drawn into a canvas at about a millisecond a frame, then handed to Meet as a camera track.

  6. 06show
    navigator.getUserMediapatched

    getUserMedia, replaced

    Patched before Meet's own JavaScript runs. Meet enumerates a camera and a microphone and cannot tell they aren't hardware.

Why hearing works the other way round

Chrome routes decoded remote audio to the output device and never hands it back to WebAudio, so intercepting tracks in the page is a dead end — it attaches happily and delivers silence. Recording the sink monitor instead also means Tekki can't hear itself: its voice leaves by the microphone track and never reaches a speaker.

Why not a reasoning model

Measured over the same three turns: qwen3:4b took 23s to answer cold, then returned empty replies at 38s and 40s. On a reasoning model the token budget covers the scratchpad first, so a cap sized for two spoken sentences gets spent thinking.

turnqwen3:4b3B, no reasoning
first, cold23.4s10.1s
second38.1s · empty0.32s
third40.8s · empty0.32s

The face

Why a cartoon
robot, exactly

Not to save effort, and not as a placeholder for something photoreal later.

  • 01

    It can't be mistaken for a person

    Anyone glancing at the call can see there's a bot in it. That's a promise the consent section makes, and an antenna is what keeps it.

  • 02

    No human character is neutral

    Any face implies an age, a gender and an ethnicity, and every viewer reads something into those. A robot sidesteps the question entirely.

  • 03

    Photoreal is a fight worth not having

    A drawing that blinks and reacts reads as charming. A nearly-human face that blinks slightly wrong reads as a corpse.

And it stays cheap — arcs and rounded rectangles, about a millisecond a frame, where a diffusion model needs hundreds. That's the whole reason local realtime is possible at all.

[neutral]
[happy]
[thinking]
[surprised]
[concerned]

Every tile is the live renderer, not a recording — the same drawAvatar that feeds the camera track in a call.

~1ms / frame

The remote control

You drive it
from Telegram

A supervisor process takes the message and forks a join session per call, so several chats can each hold their own meeting — one call per Google account.

(a Meet link)
join that call
/say <text>
say that line in the call
/status
what it's doing
/stop
leave
/signin
how to re-authenticate
T

tekki_bot

online · local

/start

Ready. Send me a Meet link.

meet.google.com/xyz-abcd-efg

Knocking. Admit me and I'll turn the camera on.

/say sorry, I was on mute

Said it. 4 people in the call, listening.

Try it

Five steps,
one machine

Needs Docker, and a Mac or Linux box with enough memory to hold a 3B model. No account to create, no key to buy, no endpoint to trust.

  1. 1

    Local models

    Ollama for the thinking, whisper for the listening, Kokoro for the voice.

    shell
    ollama pull llama3.2:3b
    brew install whisper-cpp
    mkdir -p models && curl -L -o models/ggml-base.en.bin \
    https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin
    # Kokoro, for a voice that doesn't sound like a satnav
    uv venv --python 3.12 .venv
    uv pip install --python .venv/bin/python mlx-audio "misaki[en]" fastapi uvicorn
  2. 2

    Start the three services

    They stay on the host, not in the container, so they keep their GPU acceleration.

    shell
    ollama serve
    whisper-server --model models/ggml-base.en.bin --port 8178 --host 127.0.0.1 --no-timestamps
    .venv/bin/python tools/kokoro_server.py --port 8179
  3. 3

    Give Tekki a Google account of its own

    Use a throwaway — it appears in calls under that account's name. Nothing in the repo reads, stores or transmits a password.

    shell
    cd bot && ./signin.sh # opens a browser at localhost:6080; sign in by hand
  4. 4

    A Telegram bot

    Talk to @BotFather, send /newbot, and put the token in bot/.env.

    bot/.env
    TELEGRAM_TOKEN=123456789:AA...
  5. 5

    Go

    Message your bot /start, then send it a Meet link. Admit it when it knocks.

    shell
    cd bot && docker compose up

Honest section

What it
can't do

  • Join a call by itself

    Somebody has to admit it.

  • Tell speakers apart

    No diarisation — everyone is one voice to it.

  • See anything

    Screen shares, slides and faces are all invisible.

  • Be anonymous

    Google refuses automated anonymous clients, so it needs its own account.

  • Use your Google account

    OAuth grants API scopes; there is no Meet API for joining a call as somebody else. That door doesn't exist.

  • Wait its turn well

    It answers everything it hears, which is fine one-to-one and rude in a group.

Consent

Tekki joins under its own name, renders as an obvious cartoon robot, and doesn't attempt to look human. Anyone can see there's a bot in the call. Any feature that made it harder to notice would be the wrong feature.

Also in the repo

A native macOS app, from before the browser worked

SwiftUI, putting the avatar in a window for OBS to capture and speaking through BlackHole. It works, and it's how the avatar, brain and voice were built — but becoming a device inside the page removed two prerequisites and all the manual setup.

swift run understudy-window
swift run understudy-brain
swift run understudy-render

Put a participant in the call
that owes nothing to a cloud

Clone it, pull a 3B model, and message a bot a link. Everything it hears stays on the machine it runs on.