DEV Community

Aarish mansur
Aarish mansur

Posted on Edited on

I built a VTU viva examiner that calms the room while I think

Presage SmartSpectra SDK Challenge Submission

This is a submission for the Presage SmartSpectra SDK Challenge

What I Built

Every VTU CSE student knows the gap between knowing an answer at your desk and saying it in front of the external examiner.

At my desk, "Explain the four conditions for deadlock" is a revision question. In the lab - record open, tube light buzzing, examiner waiting with the attendance register - the same sentence can make my brain blue-screen. Written prep checks memory. It does not let me practise the silence before my first word, saying it out loud, and settling enough to continue.

So I built Viva Mode: a local Electron app for mock Visvesvaraya Technological University (VTU) CSE lab vivas, powered by the Presage SmartSpectra SDK. The unit of practice is a syllabus topic, not a generic interview score. The session ends with a study-shaped note: which subject and module moved pulse relative to your own baseline, think time, time to first word, and whether you settled during Think - not "you seemed stressed."

Interview coaches already exist; other Presage entries already score recovery after interview questions. Viva Mode is deliberately a college viva practice room - lab-record paper, subject chips, module-linked reports - because that is the exam I actually sit for.

What a session feels like

A live-signal session walks through these screens:

  1. Consent. Plain-language card: what is measured (pulse, breathing, HRV, face talking, optional expressions), what is stored (local per-question summaries), what is never saved as video (no recordings, frames, or landmarks). You can paste a Presage key here or use one already saved. Telemetry unchecked by default. "Not a medical device." Camera only after you continue on the live path.
  2. Calibration (~60 s). "Sit like you're waiting outside the lab." The ring fills while signals warm up - pulse averages over roughly 12 s, breathing over ~30 s, HRV over ~60 s. Validation hints coach framing. Calibration finishes after the minute and at least 20 usable pulse samples; if pulse never stabilises, it extends (up to two minutes) instead of inventing a baseline, then offers Practice without signals.
  3. Ready. Baseline = medians of usable calibration samples. Examiner neutral. Press "Ask me."
  4. Think. Lab-record question card (subject code, module, topic). Breathing drives the room: more than +15% vs baseline → pacer + "patient" examiner; within +5% for 8 s → calm steps up (flicker and clutter ease, colours warm, "encouraging"). Gradual CSS --calm (0→1), not a strobe.
  5. Answer. Face talking for ≥1.2 s (or the "I'll answer now" button). Breathing adaptation freezes - breathing rate is not valid while talking. Examiner listens; pulse can still paint a ribbon. ≥4 s silence ends the answer.
  6. Between. Next question or end. Records think time, time to first word, peak pulse vs baseline when available, whether you settled, and recovery if the pacer appeared and you settled in the same Think.
  7. Calm Report. Per-question subject/module cards + Chart.js timeline. Relative copy only ("vs your baseline"). Local examiner remark from the state machine - no cloud model. Footer: practice/wellness tool, not medical advice. Export as PNG if you want to keep it.
  8. Settings. Telemetry for the next session, delete all data, camera/key, reduced-motion hooks.

There is also a labelled Practice without signals path: same question deck and timing, no camera, no vitals, no saved report. Useful in a lab without a webcam. It cannot stand in as SDK proof.

Question bank and honest status

data/questions.vtu-cse.json ships 41 VTU CSE viva prompts under the 2022 scheme codes:

  • BCS303 Operating Systems (8)
  • BCS304 Data Structures (9)
  • BCS401 Algorithms (8)
  • BCS403 DBMS (8)
  • BCS502 Computer Networks (8)

Modules are named (for example "Module 3: Deadlocks"), not placeholders. Each live viva draws five questions round-robin across subjects so a session is never five OS prompts in a row.

This is an early challenge build (work started on or after 4 Nov 2026), not a finished validation study. The state machine has camera-free unit tests; npm run smoke drives consent → practice → five questions → report → settings and writes screenshots under smoke/. Live-signal demo video and "what broke under my tube light" notes land once I finish a clean camera run. No invented BPM values. Viva Mode is not an interview coach, a real examiner, college surveillance, or a medical device - just a private practice room for the minute between "I know this" and "here is my answer."

Tech stack and architecture

Layer Choice Why
Desktop shell Electron 43.2.0 Matches how Presage's Node SDK is meant to run: native work in main, camera UI in a sandboxed renderer
Physiology SDK @smartspectra/node-sdk 3.4.0 Official SmartSpectra Node/Electron path for this challenge
Language TypeScript 5.9 Shared types for decoded metrics, snapshots, and session records
Bundling esbuild Separate bundles for main, preload, and renderer
Tests node:test State-machine tests without a webcam; smoke harness for the Electron UI
Charts Chart.js 4 Calm Report timeline
Runtime Node.js 22+ README requirement

No React/Vue in the MVP - HTML + CSS driven by --calm and a small render loop keeps the physiology path easy to audit.

Repo layout:

viva-mode/
├─ PLAN.md  README.md  package.json  tsconfig.json
├─ data/questions.vtu-cse.json
├─ scripts/   # build + smoke
├─ smoke/     # consent / room / report / settings screenshots
├─ src/main/main.ts            # BrowserWindow, camera permission, SmartSpectra IPC
├─ src/main/storage.ts         # local sessions under userData
├─ src/preload/preload.ts      # SDK preload + window.viva bridge
├─ src/renderer/               # DOM UI, styles, SmartSpectraSDK wiring
├─ src/shared/types.ts
├─ src/shared/signals.ts       # decode → Snapshot, confidence gating, EMA
├─ src/shared/stateMachine.ts  # phases, thresholds, hysteresis, cooldowns
└─ test/stateMachine.test.ts
Enter fullscreen mode Exit fullscreen mode

Main. Sandboxed BrowserWindow (contextIsolation, no nodeIntegration). bindSmartSpectraIpc owns the native runtime; camera permission is video-only for our page. The key comes from consent, Settings (safeStorage / OS keychain), or PRESAGE_API_KEY - never baked into the bundle.

Preload. esbuild-bundled (sandboxed preloads cannot freely require node_modules); window.viva via contextBridge. Frames stay on the SDK MessagePort bridge.

Renderer. SmartSpectraSDK with apiKey, requested metrics, and enableTelemetry: false by default. Metrics → Snapshot → step(...). The UI only paints what the reducer says.

Signal path:

Camera → SmartSpectraSDK (renderer) → MessagePort / SDK preload
      → bindSmartSpectraIpc + native runtime (main)
      → metrics → decode → Snapshot → stateMachine.step
      → Room UI / report → local userData sessions (summaries only)
Enter fullscreen mode Exit fullscreen mode

signals.ts gates usable samples (stable && confidence >= 50, pulse roughly 40-110) and applies EMAs (τ ≈ 6 s pulse, τ ≈ 10 s breathing). The SDK metrics event is windowed; the app takes the newest sample and de-duplicates on SDK timestamps. performance.now() drives the state machine; SDK epoch-µs timestamps drive EMA dt.

Phases:

consent → calibrating → ready → think ⇄ answer → between → report
Enter fullscreen mode Exit fullscreen mode

Room adaptation runs in Think only. Missing breathing drifts calm toward 0.5 and greys the chip - no punishment for unreadable data.

Demo

Live-signal demo video: [Will update later]

I am recording a clean run through the live SmartSpectra path (not Practice without signals). When the link is ready, it will replace the placeholder above and show:

  • consent with the telemetry checkbox left off by default;
  • calibration until chips ungrey with usable confidence;
  • one Think → Answer transition with live values, the pacer, and the adaptive room;
  • one module-linked Calm Report;
  • Settings → Delete all my data;
  • an honest signal-quality failure if one shows up (breathing greying while I talk, or a validation hint under bad lighting).

I will not use Practice without signals, mocked readings, or an AI-generated person as SDK proof. Until the video is embedded, judges can clone the repo, paste a key on consent (or set PRESAGE_API_KEY), and run the live path locally. Public hosting is optional. The smoke/ screenshots document the UI shell on the practice path - not a substitute for the live-signal video.

Code

Viva Mode

A lab-viva practice room for VTU CSE students. The examiner asks real questions from the 2022 scheme; the room softens as your breathing settles back toward your own baseline; afterwards you get a private Calm Report.

Built with the Presage SmartSpectra SDK for the SmartSpectra SDK Challenge.

The viva room

What it does

  • Consent first. The camera turns on only after you press the button. The screen explains what is read, what is stored, and what leaves the laptop.
  • 60-second calibration. SmartSpectra reads pulse, breathing and HRV while you sit still. The median of that window becomes your baseline — there is no good or bad number.
  • Five real questions. The deck is drawn round-robin from Operating Systems (BCS303), Data Structures (BCS304), Design and Analysis of Algorithms (BCS401), DBMS (BCS403) and Computer Networks (BCS502).
  • An adaptive room. When breathing runs above your baseline the room tightens: the tube light flickers, the…

Repository: AarishMansur/Viva-Mode - public, MIT.

README.md covers setup; PLAN.md documents the state machine, thresholds, and copy rules. Short version:

git clone https://github.com/AarishMansur/Viva-Mode.git
cd Viva-Mode
npm install
# Node.js 22+. On Windows npm 11 you may need:
#   npm install-scripts approve koffi protobufjs esbuild
PRESAGE_API_KEY=your_key npm start
# or paste the key on the consent screen / save it in Settings → Camera

npm test            # camera-free state-machine tests
npm run typecheck
npm run smoke       # Electron UI path: practice → 5 questions → report
Enter fullscreen mode Exit fullscreen mode

Get a key at physiology.presagetech.com. Never commit it. Supported platforms follow the SDK (webcam required for the live path). There is no hosted account or judge login - clone, key, run.

How It Responds to Human Signals

I wanted each SmartSpectra signal to have a job inside the viva, not five charts on a wellness dashboard. The SDK's windows forced the design: you cannot honestly animate a 60 fps "stress meter" from a 30-second breathing average, and you cannot keep driving the room from breathing mid-sentence.

SmartSpectra signal Window / constraint What Viva Mode does with it
cardio.pulseRate ~12 s average; usable roughly 40-110 BPM Compared to personal calibration baseline; feeds the pulse ribbon and per-question peak / delta in the Calm Report. Never treated as an instant reaction to a single word.
breathing.rate (+ upper trace for the pacer shape) ~30 s average; invalid while talking Drives Think-phase pacer and room calm vs baseline. Hidden/greyed when talking, warming up, or low confidence.
cardio.hrv (RMSSD, Baevsky, etc.) ~60 s window Calibration baseline context and report context when usable. Not expected as a fresh value on every UI frame; Baevsky only shown relatively ("↑ vs your baseline"), never as a raw "stress score."
face.talking Face-based detection (not an audio transcript) Separates Think from Answer: ≥1.2 s talking starts Answer; ≥4 s silence ends it. Manual "I'll answer now" is also available.
face.expression (optional) Needs a roughly forward face Soft presentation context for examiner mood alongside breathing/talking. Never treated as proof of fear, honesty, confidence, or subject knowledge.
Confidence, stable, validationStatus SDK quality gates Chips grey when confidence is low, stable is false, pulse is out of range, breathing is read while talking, or validation is not OK. Missing data drifts the room toward neutral instead of punishing the student.

Why Think and Answer are separate

The SDK documents that breathing rate is not valid while the person is talking. If I had built one continuous "calm meter" through the whole answer, I would be animating a number the sensor itself refuses to trust. So Viva Mode splits the viva the way a real one feels:

  • Think is quiet. Breathing can guide the pacer and the room. Recovery moments (pacer shown → settled within the same Think phase) are meaningful here.
  • Answer is speech. Talking detection owns the phase change. Breathing adaptation freezes; pulse can still paint a ribbon. The examiner listens instead of "coaching" mid-sentence.

That split also keeps Viva Mode different from a generic interview recovery scorer: rehearse the pause before you speak; don't grade how fast you come down after every sentence.

Adaptive rules (relative to your baseline)

Baseline = median of usable calibration samples (pulse, breathing, RMSSD, Baevsky when available). Magic numbers live in T inside stateMachine.ts:

  • Usable: stable && confidence ≥ 50 (+ pulse in range).
  • Pacer: smoothed breathing > +15% vs baseline, with a 20 s show/hide cooldown.
  • Settled: within +5% for 8 s, then calm rises by at most 0.08 per step (hysteresis between 5% and 15%).
  • Examiner mood cooldown: 6 s (neutral / curious / encouraging / patient).
  • Pulse spike (report only): peak smoothed pulse > +10% vs baseline - descriptive, not a grade.
  • Pulse fallback: only if breathing was never calibrated. If breathing was calibrated and then goes stale, calm drifts to 0.5; it does not invent calm from pulse.
  • Fallbacks: weak calibration → extend + hints, then Practice without signals; unstable HRV → omit from live UI; credit/network errors → honest banners.

Architecturally, Electron + bindSmartSpectraIpc connects the native SDK through main. The renderer decodes packets and hands ordinary snapshots to a small reducer. That reducer - not a cloud model - decides the phase, pacer, room, and examiner response. The examiner's remark is computed locally by remarkFor.

Prize Categories

I am entering both categories, while remaining eligible for an overall winner spot.

Most Creative or Delightful Use

I wanted the examiner room itself to participate. Viva Mode turns the familiar VTU ritual - lab record, waiting examiner, the question you know but cannot start - into a room that softens while you think. Pacer, disappearing clutter, warm lamp at high calm, and examiner moods make the signal response part of the stage, not a wellness cockpit beside a chatbot.

I am not claiming to invent camera PPG or to be first to wire Presage to an interview. The creative hook is the specific ritual: Think before Answer, lab aesthetics, syllabus-linked calm - on top of honest sensor constraints.

Best Thoughtful Real-World Use

This is for students like me who rehearse alone and need more than "be confident":

  • Syllabus-shaped feedback by subject code and module (BCS303 Module 3, not only "Deadlocks") - next action is "revise that module," not "meditate more."
  • Think/Answer separation respects SmartSpectra and how vivas feel.
  • Missing signals ≠ judgement - grey chips and neutral drift beat fake precision.
  • Local control - session summaries on-device, encrypted key in the OS keychain, delete-all, telemetry opt-in, no grading/employer mode.
  • Practice only - no marks, no diagnosis, no monitoring for a college.

Creative category = the room that softens. Real-world category = the VTU student practising the minute before they speak, with a report that points back to the syllabus.

Consent and Data Handling

  • Camera consent comes first. The consent screen explains what is read, what is stored, and what leaves the laptop. Camera access begins only after you choose the live path. Practice without signals never opens the camera.
  • No video is saved. Frames are processed on this laptop by the SDK's native engine and are never written to disk. Viva Mode does not save recordings, frames, or facial-landmark data. Talking detection is face-based; the app does not create an audio transcript.
  • Session data is local. Per-question summaries (think/answer timing, peak pulse vs baseline when available, breathing-while-thinking summary, settled?) are stored under the OS user-data directory for the Calm Report. There is no app account and no cloud session sync owned by Viva Mode.
  • API key handling. The key can be pasted on consent, set as PRESAGE_API_KEY, or saved in Settings. Saved keys are encrypted with Electron safeStorage (OS keychain). The key is never baked into the renderer bundle.
  • Retention and deletion are explicit. Saved sessions remain until you remove them. Settings → Delete all my data removes sessions, reports, and the saved key. There is no second app-maintained cloud copy to chase.
  • "Local" does not mean "network-free." The SmartSpectra SDK contacts Presage to validate the developer key and to compute metrics. That is distinct from Viva Mode's local session storage. I will not describe the SDK as though it never connects to Presage.
  • SDK telemetry is opt-in. Viva Mode keeps telemetry off by default. When enabled, Presage's optional telemetry sends an aggregate session summary (SDK version, platform, frame throughput, validation-warning counts) - not frames, metric values, or personal identifiers. Telemetry is not bundled silently into camera consent.
  • No optional AI service is used in the MVP. Examiner behaviour and the report remark come from the local state machine. Nothing is sent to an LLM gateway.
  • Practice mode is labelled and limited. It creates no live measurements and cannot be mistaken for SDK proof.
  • No diagnosis or assessment. Camera-derived measurements are uncertain and affected by lighting, motion, framing, talking, clothing, and flicker. Viva Mode uses them for personal practice and general reflection only - not diagnosis, treatment, medical decisions, grading, hiring, or covert monitoring. A higher pulse or Baevsky value vs baseline is not proof that someone is anxious, unprepared, or unwell. Footer copy: wellness/practice tool; SmartSpectra metrics are not FDA-cleared.

Top comments (0)