Skip to content

Software engineering · Feb 2025–present

HealthCam

A modular platform that turns webcam video into measurements alongside desktop app usage. Heart-rate estimation and blink detection run as replaceable local analyzers; a cloud dashboard makes their results available over time.

My contribution · end-to-end design and development

Backend & systems
Python services, async orchestration, and authenticated APIs.
Data engineering
PostgreSQL, incremental SQL aggregation, and weighted metrics.
Web product
User-scoped Next.js dashboards and a read-only demo.

Choose “Explore the demo” on the sign-in page. No account or webcam required. Read-only synthetic data.

My role
End-to-end design and development
Stack
Python · FastAPI · TypeScript · Next.js · PostgreSQL · Docker
Origin
BSc thesis · University of Milan
Status
3.0 · Plugins · Developer preview
BPM dashboard with three callouts: daily trends, application comparisons, and monitoring duration
  1. Trends over time

    Daily averages of estimated heart rate.

  2. App comparisons

    Measurements paired with active-application context.

  3. Data coverage

    Monitoring duration helps interpret the results.

Synthetic demo data. Demonstrates the product and its queries; does not establish measurement accuracy.

01 · Architecture

Capture once. Run multiple analyzers.

HealthCam 3.0 routes each webcam segment to enabled analyzers in parallel and aligns their results with active-app context. A service registry discovers capabilities; manifests configure native processes and Docker containers. Structured results go to an authenticated cloud API.

On the computer · Python / FastAPI

Webcam → 20-second segments

Local video and timestamp metadata

Orchestrator + service registry

Manifests: runtime, lifecycle, and capabilities

Same segment → parallel analysis

Heart rate · pyVHR

Replaceable plugin

Blink · MediaPipe

Custom blink processing

Results + app context → queue

App-Tracker → events aligned to segment timestamps

Results and context

Authenticated JSON · no video

In the cloud · Next.js / PostgreSQL

Ingestion API

API key → user · Zod validation

Raw telemetry · JSONB

Results, context, and metadata

GitHub Actions → SQL summaries

Incremental every 5 min · daily reconciliation

User-scoped dashboards

Trends, comparisons, and coverage

Current 3.0 architecture. The analysis pipeline accepts plugins; the SQL summaries and dashboard shown are the currently implemented BPM path.

02 · Engineering decisions

Designing for replaceable components

I built the capture, orchestration, service lifecycle, ingestion, database pipeline, and web application. The heart-rate analyzer integrates pyVHR; the blink analyzer uses MediaPipe landmarks with my own processing logic.

Local inference, cloud history

Video stays on the device; the cloud holds results that can be explored over time.

Trade-off: Manual setup and hardware constraints: the current BPM analyzer requires NVIDIA/CUDA.

Native processes and containers

Capture and context use OS APIs; containers isolate analysis dependencies.

Trade-off: More environments to start, connect, and diagnose.

Manifests and a shared contract

A new analyzer declares its runtime, commands, and capabilities; the registry makes it discoverable by the orchestrator.

Trade-off: New metric-specific SQL and dashboard interfaces still need application code.

Research components integrated: pyVHR / PHuSe Lab for heart rate; MediaPipe for face landmarks. I did not develop their underlying algorithms or models.

03 · Data engineering

Prepare summaries before the dashboard needs them

Raw JSONB telemetry is retained in PostgreSQL. GitHub Actions runs incremental SQL aggregation on a five-minute schedule and a daily reconciliation job. Dashboards query summary tables instead of repeatedly scanning the full telemetry history.

  1. Every 5 minutes

    Incremental aggregation

    A cursor selects new payloads. SQL upserts update sums, counts, extrema, and duration by user, day, and app.

  2. Daily · 02:10 UTC

    Reconciliation

    Rebuilds recent summaries from raw records, starting two days before the latest summary date, to include delayed data within that window.

  3. On dashboard reads

    Query summary tables

    Averages are weighted by sample count: days with little data do not count as much as days with many samples.

Precomputation keeps historical scans off the dashboard request path. The trade-off is delayed freshness and maintaining both raw records and derived summaries.

Transformation assumptions and limits

Each segment is attributed to its last app-context event and bucketed by send time (or receipt time), rather than split across app switches. Capture timestamps remain in the raw payload. Reconciliation covers a recent window; it does not automatically repair arbitrarily old late arrivals. Scheduled jobs can be delayed; no performance improvement has been benchmarked.

Engineering evidence · 30 September 2026

15 passing tests · data contract and API

6 API checks and 9 data-contract checks, with mocked database access. Cover authentication, validation, write requests, and rate limiting; not the full pipeline.

Results, example test, and reproducible report
Authentication
Missing or unknown key → 401.
Validation
Malformed JSON or a contract-invalid payload → 400.
Ingestion
Valid payload → 201 and a write call tied to the authenticated user.
Request limit
Cap reached → 429, Retry-After: 60, and no write call.
Inspect a test: rejecting requests over the cap

Excerpt from the existing test. The database mock returns a count at the cap; assertions check the response and absence of writes. The key shown is a dummy value.

db.user.findUnique.mockResolvedValue({ id: "user-1" });
db.recordingPayload.count.mockResolvedValue(INGEST_RATE_LIMIT.max);

const response = await POST(
  request(validCloudRequest(), { "x-api-key": "valid-key" }),
);

expect(response.status).toBe(429);
expect(response.headers.get("Retry-After")).toBe("60");
expect(db.recordingPayload.create).not.toHaveBeenCalled();

Scope: isolated contract and API tests. They do not verify real PostgreSQL persistence, concurrent requests, agent retries, or measurement accuracy. The contract accepts an analyzer error result; dispatching that result from the agent is not verified by these suites.

Download the report: results, revision, and reproduction (Markdown)

04 · Research & evaluation

Evaluating one analyzer within the platform

For my thesis, I integrated pyVHR, a PHuSe Lab research package from the University of Milan, as the webcam heart-rate analyzer (rPPG). HealthCam’s capture, orchestration, storage, and dashboard layers are separate from that estimator’s accuracy.

Thesis validation workstation with webcam, dashboard, and Arduino sensor on the desk
Historical context: HealthCam 2.0 thesis setup, slide 8. The current version is 3.0 with plugins.
Measurement error exceeded the differences being studied

Mean absolute error≈ 11.6 BPM

App-level differences≈ 3 BPM

I aligned estimates with a Polar H10 reference and examined error, agreement, and lighting. The tested setup could not reliably resolve these differences; the finding applies to that estimator configuration and experiment.

Source: HealthCam 2.0 thesis presentation, slides 10–11.

Replaceable analyzers were an architectural goal of the project. The experiment limits how this estimator can be used; another analyzer can reuse capture, orchestration, and ingestion.

Method, sources, and reproducibility limits

05 · Data handling

Video stays on the device

The agent uploads results, application/window context, timestamps, and file metadata. Video and sidecar files are deleted after successful analysis and upload; failures can retain them locally. Derived data is still sensitive. Local APIs currently require a trusted development machine because they lack authentication.

What you can evaluate today

The system, the result, and the next steps.

2.0 · Thesis: separated services and initial rPPG evaluation. 3.0 · Today: manifests, a capability registry, and multiple parallel analyzers.

Implemented

  • Webcam capture and active-app context
  • Parallel rPPG and blink analyzers
  • Manifest-configured local services
  • Authenticated ingestion, PostgreSQL, and BPM dashboard

Still in development

  • Packaged installer for end users
  • Service installation through the Store
  • Complete blink dashboard and alerts
  • Durable dispatch, idempotent ingestion, and broader integration tests
Current reliability limits

The dispatch queue is in memory. One failed analyzer prevents the entire segment from being sent, even when other analyzers succeed; failed uploads are retried. Durable queuing, partial-result handling, and idempotent ingestion are next steps. The published tests do not verify the complete webcam/GPU/database path.

The demo demonstrates the interface. Local execution requires private repository access, manual configuration, and compatible hardware. An experimental thesis and portfolio project, not a medical device.

Product website

Contact

Let’s talk about a role, a collaboration, or an idea worth building.

If you are considering my profile, would like to collaborate, or have an operational problem to solve, write to me. We can start from the context even if every answer is not clear yet.

Or email me directly diego.webwork@gmail.com

Verona and Northern Italy. Available for employment and freelance projects. Open to relocation.

Draft an enquiry

This opens your email app with a message to diego.webwork@gmail.com. No account is required.