ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Apuntá la cámara al sefer y te dice dónde está abierto y cuántas columnas faltan para la lectura.
Translation pending
EducationTypeScript · PythonGitHub @mateknoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Find your place in a Sefer Torah with your phone camera, and navigate to the reading
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Find your place in a Sefer Torah with your phone. Point the camera at the open column and NavTorah tells you the book, parashah, aliyah and verses, and how many columns to move to reach the reading you want. When it isn't sure, it says so instead of guessing.
The app is available in English and Spanish.
Two real tests, recorded with an earlier, Spanish-only version of the app. The previews play at 3x; click one for the full video.Read the full README ↗Find your place in a Sefer Torah with your phone. Point the camera at the open column and NavTorah tells you the book, parashah, aliyah and verses, and how many columns to move to reach the reading you want. When it isn't sure, it says so instead of guessing.
The app is free, with no ads and no sign-up, and available in English and Spanish. The phone reads the photo itself; only the text goes to the server.
Three real tests. The first two were recorded with the current version, which reads the column on the phone; the third, with an earlier Spanish-only version that sent the photo to Claude. The previews play at 3x; click one for the full video.
NavTorah doesn't recognize the image; it locates text. First the column is transcribed, errors and all. A matcher then finds that transcription in the full Torah text (80,316 words) using diagonal voting and banded alignment, with an edit distance that treats common STA"M letter confusions (ב/כ, ד/ר, ה/ח/ת…) as cheap.
The matcher decides, not the model. Confidence comes from the margin between the best and second-best candidate. Repeated passages, like the offerings of the nesiim in Bamidbar 7, come back as ambiguous with both candidates.
Positions are word indexes, so any layout works. On scrolls with the standard 245-column, 42-line layout it also gives the exact column and line.
By default the phone reads the photo itself, with no API and no cost, and sends only the text. Classical image processing (packages/vision) flattens the lighting, finds the column between its blank margins and follows each line across it, through tilt and curvature. A small neural network (a CRNN of 0.45M parameters, run with ONNX Runtime Web) then reads each line. It reads worse than a large vision model, but the matcher doesn't need a perfect reading: letters the network is unsure of come out as ?, which the matcher treats as a wildcard, so it still places the column or says it can't.
The network was trained on synthetic photos of real columns in STA"M fonts, aged and photographed in software; see tools/train. When a scan fails, the app asks whether the person wants to send that photo to improve the reader: real photos are what will help the model most.
Pick a target: the reading for a date, a holiday or special day, a parashah and aliyah, or a verse. The calendar comes from hebcal and handles each year's variants: Rosh Hashanah on Shabbat, chol hamoed, fast days, Rosh Chodesh, and special Shabbatot with a maftir from a second sefer.
After each scan NavTorah says how many columns to move and which way. It counts with the standard 245-column layout, so the count is exact on standard-layout scrolls. On scrolls with another layout it can be off, and the app doesn't detect that yet, so scan again when you get there. Once you're there, it tells you the line and the first words. Instructions can be read aloud.
After each result the app asks "Did it help?". A "no" asks what went wrong and offers to send the photo, unticked by default; after a scan it couldn't place, it offers to send the photo too. A "yes" shows how to support the app: dedicating a week (shown with that Shabbat's parashah) or a jag (shown the week before and during it). People get in touch through the contact you configure, and you add the dedication in the admin panel; the app and the landing page then show who the week is dedicated by.
At /es/admin or /en/admin, with ADMIN_PASSWORD. It shows people and scans per day, how many were placed, ambiguous or not placed, the 👍 and 👎 with their comments and photos, the latest scans with the text that was read, and the dedications, where you add and remove them. Each phone gets a random anonymous id to count people; there are no accounts and no IP addresses are stored.
Requires Docker with Compose. The app, its SQLite database and the photos sent with feedback run in one container, with the data in a volume.
cp .env.example .env # set ADMIN_PASSWORD, CONTACT_EMAIL, CONTACT_WHATSAPP, TZ
docker compose up -d --build
It listens on 127.0.0.1:3000 (BIND_ADDRESS and PORT in .env). Point your reverse proxy there, for example with nginx:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
The proxy must serve HTTPS: phones only open the camera on a secure page. If the proxy runs in another container, set BIND_ADDRESS=0.0.0.0 or put both on the same Docker network. The app limits requests per IP using X-Forwarded-For, so don't expose its port directly to the internet.
| Variable | What it does |
|---|---|
ADMIN_PASSWORD | Password for the admin panel. Without it the panel is disabled. |
CONTACT_EMAIL, CONTACT_WHATSAPP | How people reach you to dedicate a week or a jag. WhatsApp in international format, e.g. 5491112345678. |
DONATE_URL | Optional https link to a donation page (Buy Me a Coffee, Mercado Pago…). |
SITE_URL | The site's public address, e.g. https://navtorah.com. Search engines get canonical links and /sitemap.xml with it, and shared blog links show the post's cover. The blog is built ahead, so change it with --build. |
TZ | Time zone for the daily stats and for when a week starts, e.g. America/Argentina/Buenos_Aires. |
Updating: git pull && docker compose up -d --build. Logs: docker compose logs -f. The data is in the navtora-data volume (navtora.db and photos/). To back up the database while the app runs:
docker compose exec navtora node -e "require('fs').rmSync('/data/backup.db', { force: true }); new (require('node:sqlite').DatabaseSync)('/data/navtora.db').exec(\"VACUUM INTO '/data/backup.db'\")"
docker compose cp navtora:/data/backup.db ./navtora-backup.db
Requires Node 22.13+ and pnpm.
pnpm install
pnpm test
pnpm dev # Next.js with local HTTPS, to test the camera from a phone on the same network
The reader needs no configuration. For the admin panel and the support options, copy apps/web/.env.example to apps/web/.env.local. The database goes to apps/web/data/ unless DATA_DIR says otherwise.
The site has a public landing page at /en and /es and a blog at /en/blog and /es/blog; the app itself is at /en/app and /es/app. A scan takes a second or two and costs nothing.
| Path | What it does |
|---|---|
packages/core | The engine, in plain TypeScript: normalization, index, STA"M-aware distance, matcher, navigation. |
packages/data | Torah text, the standard 245-column layout, and parashot with aliyot, built from tikkun.io. |
packages/vision | Image processing for the local reader: lighting, column and line detection, line crops. No dependencies. |
packages/ocr | OCR providers: local (vision + CRNN, with the model runner injected), an oracle that simulates OCR noise, and a disk cache for evaluation. |
apps/web | The Next.js PWA: camera, results, navigation, target picker, manual entry, feedback, dedications and the admin panel. |
tools/eval | Evaluation on openly licensed scanned sifrei, on simulated noise, and of the local reader; also a Claude vision reader, only to compare against. |
tools/train | Synthetic training data and training of the local reader. |
packages/data/dist is committed. pnpm build:data regenerates it from packages/data/raw.
On a scanned standard-layout sefer with known positions, every column was placed correctly. On three older or non-standard scrolls, one of them from the 15th century, every confident answer was consistent with the neighboring columns, and the app abstained on crops without readable text. With up to 50% of letters wrong in simulated OCR output, the matcher never placed a passage wrongly with confidence. Details and how to reproduce them are in docs/evaluation.md.
It has also been used on a worn sefer, a printed tikkun and a third sefer; see the demo and the field tests.
The local reader, trained only on synthetic photos, placed 241 of the 246 columns of the Shannon sefer, 240 of them with the exact column number (the rest are the nesiim and the two songs in brick layout), 225 of 226 of the Kokhav sefer, 189 of 190 of Makhon Ot and 187 of 233 British Library crops, with no wrong or out-of-order answer. It also placed all 10 real frames from the two recordings.
The photo never leaves the phone: only the transcribed text goes to the server. There are no accounts. To know whether the app works, the server keeps each scan's result, timing and transcribed text, with a random id the phone makes up; it stores no IP addresses. A photo is sent and kept only when the person ticks the box to send it with feedback, to train the reader.
The code is MIT.
@hebcal/core (GPL-2.0) and @hebcal/leyning (BSD-2-Clause) for the reading calendar. If you distribute a build of apps/web, the GPL applies to that combined work.tools/eval/src/download.ts lists their sources and licenses.