Say anything — in Russian or English, out loud or typed — and it comes back with a line. Always with the source named.
Live: https://sgauruseu.github.io/meme-reply/
- Listens. Web Speech API, no key, no backend. Russian or English.
- Answers on topic. It does not fire a random quote at you: it scores what you said against a tagged corpus and picks a line that is actually about the same thing, then shows you which words earned the match.
- Goes to the internet when it has nothing. If the local corpus has no relevant line, it fetches a fresh joke from one of three live APIs. The network is an enhancement, never a dependency — unplug it and the app still answers everything.
- Speaks the answer in a voice matching the language, and says so plainly when the device has no such voice instead of reading Russian in an English accent.
- Names every source. Author, work, licence, on every single reply.
Speech recognition is not local. In Chrome the audio goes to Google's servers. The app says so above the microphone button, before you ever press it. Firefox never implemented the API at all, so there typing is the only input — and typing is a first-class path, not a consolation.
The live joke APIs are English only. That is why the Russian side is a bundled corpus rather
than a search. Two of the best-known quote APIs, quotable.io and zenquotes.io, were tested
and rejected outright: neither sends CORS headers, so a static site cannot call them at all.
Every bundled line is public domain, and every one is attributed:
| Russian | English |
|---|---|
| Русские пословицы | English proverbs |
| Козьма Прутков | Mark Twain |
| А. П. Чехов | Oscar Wilde |
| Н. В. Гоголь | Ambrose Bierce, The Devil's Dictionary |
| И. Ильф и Е. Петров | George Bernard Shaw, Jerome K. Jerome, Benjamin Franklin |
Nothing is scraped from a meme site, nothing is a song lyric, nothing is by a living author. Proverbs carry no named author by nature, which also makes them immune to the misattribution that plagues quote collections online.
Live jokes come from icanhazdadjoke, Official Joke API and DummyJSON, each credited on the reply it produced.
- Detect the language. Two alphabets, so counting letters beats any model. Transliterated
Russian (
privet, kak dela) is caught by a short marker list. - Tokenize and stem. Suffix trimming, run twice — Russian stacks endings, so
работаюshedsюto reachработаand must shedаagain to reach the stemработеalso reaches. One pass leaves those in different buckets, which is a bug the tests caught. - Score by TF-IDF. A term shared with two entries out of eighty says far more than one shared with thirty. Tags count double: they are editorial judgement about what a line is about, while the body text merely contains words.
- Pick among near-ties at random, so the same input does not always return the same line, and exclude the last six replies so no proverb comes back twice running.
Randomness is a parameter, not a call to Math.random — which is the only reason a function
whose whole job is to be unpredictable can be unit-tested at all.
src/core/ pure: language detection, stemming, matching — no network, no DOM, no clock
src/corpus/ the bundled lines, with tags and attribution
src/adapters/ speech recognition, speech synthesis, live APIs, localStorage
src/ui/ React, presentation only
ui → adapters → core, never the reverse, enforced by an ESLint rule rather than by good
intentions. 55 unit tests cover the core, including regression cases for the two bugs the
suite actually caught: the Russian stemmer splitting word forms, and function words like need
hijacking matches ("I need more coffee" once answered "a friend in need is a friend indeed").
npm install
npm run dev # http://localhost:5173
npm test # unit tests
npm run typecheck && npm run lint && npm run buildNode 22 or newer. Chrome or Edge for voice; any browser for typing.
No backend, no account, no analytics. Settings and conversation live in localStorage, under
keys prefixed meme-reply: — every GitHub Pages project site on an account shares one origin,
so an unprefixed key would collide with the neighbouring app. Outbound requests: the live joke
APIs, and whatever your browser's speech recognition does with your audio.
Code is MIT licensed — see LICENSE.
Specification in PRD.md; the agent working agreement is in CLAUDE.md.