ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
태국 오픈 카메라 기반 컴퓨터 비전 공개 교실, 태국어·영어로 학습·모델 훈련·실험, 브라우저에서 실행
A public classroom for computer vision, built on Thailand's open cameras. Learn, train your own model, play — in Thai and English. Everything runs in your browser.
교육JavaScript · HTMLGitHub @Nonarkara온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기VISION — how a computer sees · คอมพิวเตอร์มองเห็นอย่างไร. A bilingual computer-vision classroom on Thailand's public cameras and your own webcam. Live: vision.nonarkara.org
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Live: vision.nonarkara.org
A public, bilingual (ไทย / English) classroom for computer vision. Visitors point the machine at Thailand's open road cameras — or at their own camera or photos — and watch it go from pixels → numbers → edges → motion → "car 68%".
Every model runs in the visitor's browser (TensorFlow.js and MediaPipe, self-hosted weights). The server never looks at a picture: it serves the site, publishes the camera catalogue, and relays still frames from allow-listed hosts in memory only.
This repository is also a textbook. The Vision handbook explains computer vision from pixels to trained models, chapter by chapter, using the exact code that runs the site — with worked numbers, diagrams, a Thai summary and a self-serve exercise in every chapter, and eight runnable lessons that need nothing but Node 22. The site publishes the same chapters as a browsable library at /handbook:
node examples/01-pixels.mjs # a picture is a grid of numbers
node examples/03-convolution.mjs # one 3×3 step, worked by hand
node examples/07-transfer-learning.mjs # teach a model — and watch it learn the wrong lesson
| Chapter | Run | |
|---|---|---|
| 01 | Pictures are numbers | examples/01-pixels.mjs |
| 02 | Light, colour and thresholds | examples/02-threshold.mjs |
| 03 | Convolution and edges | examples/03-convolution.mjs |
| 04 | Motion | examples/04-motion.mjs |
| 05 | Neural networks | — |
| 06 | Object detection | examples/05-detection-nms.mjs · examples/06-precision-recall.mjs |
| 07 | Teaching a machine | examples/07-transfer-learning.mjs |
| 08 | Limits and ethics | examples/08-catalogue.mjs |
System design: architecture · the frame relay · browser runtime · operations · CV-as-a-service blueprint (proposed). Reference: models · API · glossary ไทย/English · reading list · the ultimate sources · superpowers · browser actions.
Every PNG in the handbook is generated by the examples (npm run figures) from synthetic scenes — no real camera frames, so no real people; the six diagrams in docs/img/*.svg are drawn by hand. tests/examples.test.js fails if the code ever stops printing a number the handbook quotes.
Smart-city work took Dr Non Arkaraprasertkul through city operations centres (IOCs) in Thailand and abroad — Nakhon Si Thammarat, Patong, Rawai, New Taipei, Shenzhen, Los Angeles. They looked alike: a wall of screens, hundreds of camera feeds, a handful of operators. And people being people, someone was dozing, someone was texting, someone was on a long break.
Which raised the question: how do we know the humans didn't miss anything?
That was his first understanding of what computer vision should be — not a machine that replaces the operator or knows who anyone is, but an eye that doesn't blink, watching every screen and tapping a human on the shoulder: this one, now — always with a confidence number, because machines miss things too.
An early spark came from conversations with Dr Supakorn Siddhichai, who trained in computer vision at Imperial College London decades ago and is now acting CEO of depa, where Dr Non works. The curiosity was his to light; the learning, the design and every line of this system are Dr Non's. The full story, with the photos from those control rooms, is at /story.
| Dr Non Arkaraprasertkul · creator and builder | Senior Expert, Smart City Promotion Department, Digital Economy Promotion Agency (depa). Initiator and steward of FloodDash — the camera wall this classroom runs on. |
One page owns it: public/css/vision.css. Colour is four hues from Sanzo Wada's archive — Ivory Buff, Deep Indigo, Neutral Gray, Jasper Red — and no fifth hue is admitted. Each hue also carries a deep and a pale step built on one shared ladder — 24% hue + 76% black, 22% hue + 78% white — so a pale Indigo and a pale Ivory Buff are visibly the same move rather than two accidental colours.
Every pairing the CSS is allowed to set is measured, and every forbidden pairing is proved illegible rather than assumed:
npm run palette # print the ladder and 21 measured contrast ratios
The ladder buys one real freedom: base Jasper Red on Ivory Buff is 2.48:1 and was always banned as text on paper, while jasper-deep clears 11.30:1 — so emphasis set in signal on the paper ground is legal now, same hue, same job, one step down. npm run check fails if that ever stops being true.
Machine light is the other freedom, and it is still the same hue. Jasper Red may glow — a soft shadow of #eb5324, never a fifth colour and never a gradient fill — only on what the machine sees: a detection, a motion mark, the lens that is reporting one. A trail fades by opacity. prefers-reduced-motion keeps the mark and drops the pulse. npm run palette does not gain a pairing from that glow.
Type is eight steps rather than three, and the playfulness comes from mixing two voices cut from one superfamily — IBM Plex Sans and IBM Plex Sans Thai for everything a person wrote, IBM Plex Mono for every number, label and confidence the machine reported — in named pairings (.stem, .measure, .mix) rather than from arbitrary sizes. A proportional letter and a fixed-pitch letter are one alphabet read two ways, which is the argument of the site made in the typeface rather than in a caption under it. Because the Thai face is a member of the same family, a bilingual heading is one design instead of a condensed Latin poster stacked above a wide Thai sans.
public/js/drive/street.js builds everything beside the road from the track's own centreline, in real metres: kerbs at 3.2 m, footways 2.6 m deep with a 15 cm upstand, shophouses 1.2 m behind them, lamps every 30 m at 8.5 m, utility poles every 45 m at 9 m, bicycles and motorbikes at the kerb.
The rule the room is built on: the simulated camera may only name things the site's real detector can name. Every detectable item carries a real COCO id and the same Thai name the model prints in ml/labels.js, and tests/street.test.js checks that against the real table. A lamp post is 8.5 m tall, three metres from the car, and COCO has no category for it — so it is drawn and never given a box. That gap is the lesson, not a defect.
| URL | Room | What you can do |
|---|---|---|
/ | Home | One live camera, five lenses: picture · numbers · edges · motion · objects. "Try it on your own camera." |
/learn | 01 Learn | Eight chapters, each with a live instrument: pixels, colour channels, thresholds/Otsu, 3×3 convolution, Sobel edges, frame differencing, a detector with a confidence slider, and what it cannot do. |
/cameras | 02 Cameras | Tile-free dot map of ~3,400 cameras from 7 sources; filter, study any readable camera, run a polite census. |
/train | 03 Train | Guided lessons on one video: label frames (supervised nearest neighbours), group unnamed frames (two-cluster k-means), reward alert actions (one-step contextual bandit). The default clip is real iTIC footage (busy Rama IV, quiet Si Sothon), plus public-camera switching and local video files; optional object teaching, trained layer and diagnostic tools. |
/gesture | 04 Gestures | Teach 2–4 gestures (one finger, palm, …), bind each to a small in-browser action (beep, buzz, flash, lamps, tone, link); honest limits of what a page may do. |
/drive | 05 Drive | A real street, not a bare track: kerbs, footways, buildings behind them, street lamps, utility poles, parked bicycles and motorbikes, hydrants, benches, planters, people and dogs. Two/four lanes, roundabout or Bangkok-inspired city; 6–24 cars. Click any car for its moving driver's view, with detection boxes and computed acceleration. Day, rain, night, sun glare. Written rules and simulated detections, not a trained driving system. Behind a session gate of real iTIC frames: select every tile with a car, motorbike or person, then a short note on supervised labelling. |
/games | 06 Games | Count race vs the detector, fewest-pixels guessing, and "fool the machine" on your own webcam. |
/everyday | 07 Everyday | Face unlock, QR, OCR, X-rays, lane keeping, self-checkout, crop apps, traffic cameras — how each works and fails. |
/colour | Colour room | The four plate hues dealt onto four jobs, 24 ways. A machine reads four numbers and a ratio; a person reads a feeling. Deal, answer one word, see what people say. Six plates the machine refuses are behind an explicit button. |
/research | 08 Research |
flowchart LR
subgraph Owners["Camera owners (7 public sources)"]
HLS["Live HLS<br/>iTIC · ipcamlive<br/>(CORS allowed)"]
JPG["Still JPEGs<br/>DOH · municipal · DWR<br/>(no CORS)"]
end
subgraph Mac["This Mac"]
FD["FloodDash :8340<br/>/api/cctv/all<br/>(aggregates sources)"]
subgraph V["vision :8430 (Node, no deps)"]
CAT["catalog.js<br/>classify: video / still / view / off"]
REL["relay.js<br/>memory-only frame relay"]
HTTP["http.js<br/>pages · CSP · rate limits"]
end
T["cloudflared<br/>vision tunnel"]
end
subgraph Browser["Visitor's browser — all computer vision happens here"]
SRC["source.js<br/>public camera · webcam · photo"]
CV["cv/ops.js + lenses<br/>pixels · edges · motion"]
ML["TF.js<br/>SSDLite MobileNetV2 (COCO)<br/>MobileNetV2 embeddings"]
end
WEB["Your webcam / photo"]
FD -->|every 10 min| CAT
JPG -->|allow-listed hosts only| REL
CAT --> HTTP
REL --> HTTP
HTTP --> T -->|https| SRC
HLS -->|directly, never via us| SRC
WEB -->|never leaves the tab| SRC
SRC --> CV --> ML
sequenceDiagram
participant B as Browser
participant V as vision server
participant C as Camera owner
B->>V: GET /api/frame?id=maholan:123
Note over V: id must be in the catalogue<br/>URL comes from a server-side map,<br/>never from the request
alt cached < 30 s
V-->>B: frame from memory
else
V->>C: fetch (https, no redirects, 9 s timeout,<br/>≤4 in flight, ≤2 per host)
C-->>V: image/jpeg ≤ 4 MB
V-->>B: frame (kept in RAM 30 s, never on disk)
end
Note over B: lenses + models run here,<br/>nothing is sent back
/colour can send one word from a fixed list of ten, plus which of the 24 plates you were looking at. Both fields are enums the server fixes in advance, so the request has nowhere to carry a name, an address, or a sentence — the privacy property is structural, not a promise. Consent is a checkbox ticked per answer and it is never stored. No cookie, no identifier, no IP retained (the rate limiter is in memory and is never written down). Counts are published at /api/colour; the raw file is gitignored. The refusal on /system states this precisely rather than claiming "no analytics" outright, which would no longer be true.script-src 'self', no inline script/style, no eval (TF.js's regenerator fallback is pre-empted in ml/tf.js), media only from the three CORS video hosts the catalogue classifies as video (the allow-list is a suffix pattern, so its count moves with the catalogue — the CSP is built from that list, and ?v stamps are kept uniform on purpose even where a host serves no video).npm run dev # http://localhost:8431 (reads cameras from FloodDash on :8340)
npm test # unit tests: catalogue + store, relay, http, routes,
# sources, layer races, fullscreen, and the handbook's numbers
npm run check # build-handbook --check (staleness AND orphaned outputs) + node --test + check-site.mjs (pages, links, reachability, imports, CSP hazards, language parity, robots and sitemap, model shards, raw path safety, /api/health) + check-forked-imports.mjs + check-privacy.mjs + check-docs.mjs (handbook links, anchors, mermaid, handbook lang + emphasis) + palette-measure.mjs --check
npm run build:handbook # re-render docs/*.md as the site's /handbook section
npm run figures # regenerate the handbook's figures from examples/
npm run deploy # checks, stages the release, restarts the production app
No dependencies. Node ≥ 22. Webcam access needs a secure context: localhost or https.
| Path | What |
|---|---|
server/ | index.js routes · catalog.js FloodDash → camera kinds · relay.js memory-only still relay · http.js static, CSP, limits |
public/*.html | one file per room; partials in public/partials/ are stitched in per request |
public/js/core/ | i18n, catalogue, frame sources (public camera · webcam · photo), the shared specimen bar |
public/js/cv/ | classical CV (pure functions, tested in Node), lenses, drawing |
public/js/ml/ | TF.js loader, SSDLite detector (COCO), MobileNetV2 embedder, kNN / softmax learner |
public/js/<room>/ | each room's instruments; drive/street.js builds the street around the road and drive/captcha.js the challenge in front of it |
public/models/ | weights, cached immutable for a year — a new model gets a new folder |
public/CCTV photos/ | original IOC photographs, full resolution, with the location metadata stripped; web copies live in public/img/ioc/ |
ops/ | launchd services and tunnel routing template |
tests/ | node --test suites for the server, relay, CV ops, learner, sources, layers, fullscreen, the street and its COCO classes, gesture, focus, reward, the blink drill's arithmetic and a full drill driven through a stub DOM, and the handbook's quoted numbers. ls tests/*.test.js is the roster — it is deliberately not written down here, because a count written in prose was stale twice before this line was removed. |
scripts/ | build-handbook.mjs (renders docs/*.md to the site's /handbook section; --check fails when stale) · slug.mjs (shared heading slugs) · check-site.mjs (pages, links, reachability, imports, CSP, parity, robots/sitemap, model shards) · check-docs.mjs (handbook links and anchors) · palette-measure.mjs (prints the tone ladder and proves every colour pairing) · deploy-local.mjs |
Any instrument with a specimen bar has My camera, Switch camera (front/back on phones) and My photo. Front cameras are mirrored at the source, so every lens and model sees what the person sees. Append ?source=mine to any room's URL to start on the webcam. Frames never leave the tab.
flowchart LR
W["workspace<br/>/Volumes/Data/…/computer vision"] -->|npm run deploy:<br/>check → copy server + public| R["runtime<br/>~/Library/Application Support/Vision"]
R --> S["launchd<br/>org.nonarkara.vision<br/>127.0.0.1:8430"]
S --> T["launchd<br/>org.nonarkara.vision-tunnel"] --> CF["Cloudflare"] --> U["vision.nonarkara.org"]
Production runs as org.nonarkara.vision on 127.0.0.1:8430, behind the dedicated vision Cloudflare tunnel (org.nonarkara.vision-tunnel). The runtime is ~/Library/Application Support/Vision: launchd cannot reliably start the app from the external project volume. Service definitions and the tunnel routing template live in ops/; credentials remain in ~/.cloudflared.
Bump version in package.json on every release (it stamps asset URLs and ETags), then run npm run deploy. It checks the site, copies code and public assets without original CCTV photos, preserves the production catalogue, and restarts only the app. Verify the public /api/health afterwards.
Every canvas instrument offers Full screen. Desktop browsers use native fullscreen; narrow screens use a viewport mode with an explicit exit and isolated background controls. Training keeps the camera and example controls together. Camera credits remain visible. Escape restores the previous view; live canvases redraw at their new size. The interface is checked in Thai and English from 360px phones to 1920px displays, including landscape.
Camera owners: open an issue at github.com/Nonarkara/vision/issues with the camera's name or id and what you'd like (remove, or stop relaying frames). See /legal#takedown.
Cameras belong to the agencies that run them (GISTDA / BMA / DOH / iTIC, Nakhon Si Thammarat, Pak Kret, Rangsit, DWR, cctv.maholan.net), aggregated by FloodDash. TensorFlow.js and model weights (Apache-2.0), hls.js (Apache-2.0), IBM Plex Sans, IBM Plex Sans Thai and IBM Plex Mono (OFL). Simplified Chinese on the new Teach and Drive-gate lines uses a subset of WenQuanYi Micro Hei (Apache-2.0). Colour: four hues from Sanzo Wada's archive — Ivory Buff, Deep Indigo, Neutral Gray, Jasper Red — via Palette. Datasets: COCO (Lin et al., 2014), ImageNet (Deng et al., 2009). The Drive room's street, and the CAPTCHA that opens it, cite reCAPTCHA (von Ahn et al., 2008; Google's own description), Street View house-number recognition (Goodfellow et al., 2014) and the Intelligent Driver Model (Treiber, Hennecke & Helbing, 2000).
© 2026 Dr Non Arkaraprasertkul. All rights reserved.
/focus is opt-in, local face-landmark analysis, not identity recognition or emotion inference. MediaPipe 0.10.32 and the Face Landmarker float16 v1 model are self-hosted. A classic worker owns inference at no more than two frames per second; only its response CSP permits WASM compilation. Sessions have a maximum eight-hour wall-clock limit, bounded minute summaries and self-reported mood notes. Hidden tabs, pause, finish, navigation, stalled frames and failures release camera tracks and terminate the worker. No imagery or landmarks persist or upload. Export is an explicit local download. Threshold/calibration and eight-hour bounded-state tests use synthetic signals; browser lifecycle checks use generated video, never a private camera.
| Sixty years of papers, the ones this site runs on, open questions. |
/system | 09 System | Diagrams, relay rules, CSP, models, live health, credits. |
/legal | 10 Fine print | PDPA (B.E. 2562), ownership, what not to do, takedowns. |
/story | The story | The IOC photos (with the detector counting screens vs people), a live six-camera wall the machine watches, the blink drill — six frozen screens, one flashes for a few tens of milliseconds, and you are told your own hit rate against the machine's, and the maker. |
data/ |
cameras.json — the last catalogue FloodDash handed us; gitignored, recreated on first run |
docs/ | the handbook: eight chapters, system design, reference; docs/img/ figures and diagrams |
examples/ | eight runnable lessons on synthetic scenes; build-figures.mjs regenerates docs/img/*.png |