Case study
kyuar
QR codes that live inside Telegram: made in any chat, in a conversation with the bot, or in a Mini App editor, and scanned right there too.
- Year
- 2026
- Role
- Author
- Stack
- Next.jsTypeScriptgrammYRedis
29
module shapes, each one passes a decoder test
12
ready-made themes, none below 4.5:1 contrast
Problem
Most QR generators give you black squares on white. The ones that make prettier codes often make codes that do not scan. And the link ends up in Telegram anyway, while the code is made in a separate app.
Solution
kyuar works without leaving Telegram:
- In any chat: type
@kyuarbot https://example.comand pick a color. - In the bot chat: send any text, phone number or email and get a code back.
- In the Mini App editor: every part of the code is styled on its own: data modules, finder rings, finder eyes, background, margin, logo. Each takes a solid color or a gradient. Halftone mode draws a picture with the code itself.
- Scanner: read through the Telegram camera, from a photo, or by sending the bot a picture.
Risky links are flagged:
javascript:, plainhttp, link shorteners, names that imitate another site.
All four render through one engine, so a code made inline looks the same as one made in the editor.
Architecture
Telegram ─┬─ inline / message ─→ /api/bot (webhook) ─→ grammY
│ └─ image URL → /api/qr
└─ Mini App ─→ editor (renders in a Web Worker)
├─ POST /api/share → savePreparedInlineMessage
└─ POST /api/render → Redis (10 min) → download- One renderer everywhere.
packages/qrturns text into a styled SVG. The same function runs in the editor's Web Worker, in/api/qrand in/api/render. - URLs built for caching. The style travels in the URL as base64url JSON, holding only the
parts that differ from the defaults. The same code always gets the same URL, and
/api/qranswers withCache-Control: immutable. - One deployment. In production the bot runs inside the web app as a webhook route. The
Docker image has no build args: settings are read from
.envat startup, so one image runs in any environment. - Security. Every request made on a user's behalf verifies
initDataon the server.
Codes that scan
Three layers catch a code that will not scan:
- The renderer warns when any color is below 4.5:1 contrast against the background.
- "Scan test" decodes the design in four conditions: normal, small (132 px), dim and dark.
- In the tests every shape, gradient, logo and halftone is rendered and decoded again. A shape that looks good but does not scan counts as a bug and does not ship.
Halftone mode follows the paper Halftone QR Codes (SIGGRAPH Asia 2013). Each module is split into 3×3 parts. The centre part keeps the real value; the rest are taken from the picture and dithered with Floyd–Steinberg.
Telegram quirks
web_appbuttons only work in private chats. Anything that can land in a group opens through at.me/<bot>?startapp=…link instead.- Images in inline results have to be JPEG.
- resvg runs with
loadSystemFonts: false: QR SVGs have no text, and loading fonts would take seconds.
Next
Saved codes per user, backed by PostgreSQL.