Project Info
This project did not submit a demo video on Devpost.
A camera-based focus coach that catches you slouching or reaching for your phone, delivers a personalized AI nudge, and trains your brain to bounce back. No shame, no blockers.
Inspiration
I have ADHD, and the thing that wrecks my focus is not the distraction. It is what happens half a second after I notice it: the jolt of panic and self-blame, the shame cycle. The distraction is cheap. The emotional friction that follows is what actually costs me the next thirty minutes. This is not just a feeling. Mark et al. (2008) found that a knowledge worker is interrupted roughly every 11 minutes and needs about 23 minutes to fully return to task. If you model the share of time actually spent in productive focus as $$ P = \frac{t_{\text{focus}}}{t_{\text{focus}} + t_{\text{recover}}} $$ then with $t_{\text{focus}} = 11$ and $t_{\text{recover}} = 23$ minutes you get $P \approx 0.32$. Only about a third of the day is real focus. For people with ADHD, the recovery term is neurologically larger, and every recovery is taxed again by shame. Most productivity tools attack the wrong variable. Blockers and timers try to shrink interruptions, but the real leverage is in the denominator: cut $t_{\text{recover}}$, the time it takes to bounce back. My capstone, Anchor, was my first attempt at this. I reverse-engineered a Bluetooth smart ring and paired it with a desktop app, so that when I caught myself drifting I could click the ring and instantly receive AI-generated, context-aware encouragement instead of spiraling. It externalized the act of refocusing and logged every recovery as a win. Anchor worked, but it had a catch: it required me to notice and choose to click. aslope is the question Anchor left me with. What if the system noticed for me? What if refocusing did not depend on me having the self-awareness, in the worst moment, to act? The name is the joke and the thesis: the slow slope of a slouching spine, and the slippery slope of one glance at your phone becoming twenty minutes gone.
What it does
aslope quietly watches your work session through your laptop camera and looks for the two behaviors that most reliably mark the start of a drift: slouching and picking up your phone. The instant it sees one persist, it does not scold you. It delivers a personalized nudge that was generated for you in advance: an image, or a short line spoken aloud by an AI voice coach. You correct, you recover, and it logs the win. After each session, the system reasons about what just happened. It looks at how often you slouched, how often you reached for your phone, and crucially how fast you recovered each time, then it tunes the next session's nudges and tasks to what actually worked for you. The dashboard shows a Log of Wins rather than a tally of failures. Each user picks a framing that fits them. Reward mode shows aspirational imagery of focus and good posture. Consequence mode shows the long-term cost of the habit. Neither mode ever blames the person. The framing is always about the trajectory, never about failure in the moment.
How we built it
The entire build was done with Claude Code, and the whole architecture follows one rule: slow generation never touches the real-time path. The system runs on two clocks. The slow clock runs at the start and end of a session and handles all reasoning and asset generation. The fast clock runs continuously during the session and handles detection and reward delivery, with a latency budget under 300 milliseconds from trigger to on-screen nudge. Redis bridges the two: the slow clock fills a cache of ready-to-serve assets, and the fast clock only ever reads from it. The Anthropic API is the orchestration brain. Using the Messages API MCP connector, a single Claude call both reasons and drives generation: it writes the prompts, generates the quote pool, and calls Midjourney directly through the connector. After a session, Claude does the heavy reasoning that personalizes the next one. Midjourney generates the nudge imagery, themed to each user's interests and chosen mode. Deepgram is the voice coach, reading the nudge aloud. Top lines are pre-rendered during prep so the first nudges are instant. Redis is both the fast-clock cache and cross-session agent memory, using vector search so the coach can semantically recall past wins. Sentry wraps the long-running generation jobs, the CV loop, and the IPC layer, since the most failure-prone parts are the multi-step pipelines and the flaky third-party servers.
Challenges we ran into
The biggest design challenge was latency. Midjourney takes tens of seconds to minutes, and a nudge that arrives a minute after you slouch is worthless. Recognizing that it simply could not live in the real-time loop, and then redesigning around pre-generation plus a Redis cache, was the decision that made the whole thing feasible. The Midjourney MCP server was the flakiest integration. It is prerelease, and we hit OAuth and Cloudflare challenge errors more than once. We mitigated it by generating a real image stock early while the connection was healthy and making the cache able to serve pre-baked images so the demo never depended on a live call. Computer vision was the riskiest core, so we built it first. Posture detection only became reliable once we added a calibration step to capture each person's good-posture baseline, plus persistence thresholds and a cooldown so the system was not firing constantly or reacting to a single shift in the chair. The hardest challenge was not technical. It was making sure the consequence framing never tipped into shame, because shame is the exact thing we are trying to remove. Every generated line had to be about the trajectory, not the person.
What we learned
The deepest lesson came from Anchor and shaped aslope directly: the metric you choose can quietly sabotage the goal. Anchor's original dashboard counted clicks, but a raw count cannot tell resilience apart from dysregulation. A frantic, spiraling day and a calm, recovering day could produce the same number. That insight led to the Focus Wall idea of banking focus intervals instead of counting interruptions, and in aslope it became the principle that recovery time, not interruption count, is the real measure. Faster bounce-back is the thing worth optimizing. We also learned that passive sensing changes the psychology. Anchor required a deliberate act, which gave the user agency but also a way to fail by simply not clicking. aslope removes that dependency, but it raises a new responsibility: a system that watches you has to be gentle, or it becomes one more source of pressure. That tension drove most of our design decisions.
What's next
for aslope The research framework from the Anchor capstone still points the way. The next real step is a within-subject feasibility pilot to test whether aslope genuinely helps people redirect faster and feel less shame, grounded in established behavioral approaches to ADHD intervention rather than in how useful people merely say it looks. Perceiving the value of a tool is not the same as testing it, and recovery time finally gives us an honest number to test against.
Anchor Vision
A camera-driven focus coach for the Berkeley AI Hackathon. See PLAN.md for the full architecture.
Quick start
On WSL, use Linux-native pnpm (not the Windows shim):
corepack enable && corepack prepare pnpm@9.15.4 --activate
cp .env.example .env
# Fill in ANTHROPIC_API_KEY, REDIS_URL, and SENTRY_DSN (optional but recommended)
pnpm install
pnpm dev
WSL / Linux note
Webcam: WSL2 cannot access your laptop camera. To use the webcam, run the app on native Windows (not WSL, not \\wsl$\...).
PowerShell cannot use \\wsl$\... as a working directory (UNC paths are not supported), and WSL's node_modules installs the Linux Electron binary anyway.
Run with camera (Windows)
One-time setup in PowerShell:
# 1. Clone/copy the repo to a Windows path (not WSL)
cd $env:USERPROFILE\developer
git clone https://github.com/adam-ajroudi/Aslope.git ai-hackathon-berkeley-2026
cd ai-hackathon-berkeley-2026
# 2. Copy your .env from WSL (adjust distro name if needed)
copy \\wsl$\Ubuntu\home\adam\developer\hackathons\ai-hackathon-berkeley-2026\.env .env
# 3. Install Node 20+ on Windows if needed: https://nodejs.org
corepack enable
pnpm install
pnpm dev
Camera + fullscreen overlay will work from this Windows install. Keep coding in WSL; sync via git or copy .env when keys change.
WSL without camera: use pnpm dev in WSL and click Use demo feed in the app.
If Electron fails with libnss3.so: cannot open shared object file, install the required libraries:
# Ubuntu 24.04 (Noble) — note the t64 suffix on some packages
sudo apt-get install -y \
libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 \
libcups2t64 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
libxfixes3 libxrandr2 libgbm1 libasound2t64 libpango-1.0-0 \
libcairo2 libx11-xcb1 libxcb-dri3-0 libxshmfence1
Milestone 5
- Midjourney MCP via Anthropic connector (
mcp-client-2025-11-20) - After session prep, generates up to 4 images in the background (one per trigger/mode)
- Downloads CDN assets to local cache, serves via
nudge://protocol in overlay - Falls back to SVG placeholders if Midjourney fails or credentials missing
Requires: MIDJOURNEY_MCP_URL, MIDJOURNEY_OAUTH_TOKEN in .env
Optional: MIDJOURNEY_PREP_LIMIT=4 (max images per session)
Milestone 4
- Claude session prep via Anthropic Messages API (no MCP yet)
- Generates personalized quote pools + Midjourney image prompts per trigger/mode
- Quotes cached in Redis (
quotes:*,assets:*); image prompts stored atprep:{userId}:promptsfor M5 - In-memory fallback when Redis is down; hardcoded fallback when API key missing
- UI shows "Claude is prepping…" during session start
Requires: ANTHROPIC_API_KEY in .env
Milestone 3
- Redis asset cache — session start seeds nudges, triggers read from cache
- Session + trigger events logged to Redis (
session:*,events:*) - Falls back to hardcoded nudges if
REDIS_URLis missing or unreachable - Status panel shows Redis connection state
Redis keys used: assets:{userId}:{trigger}:{mode}, session:{sessionId}, events:{userId}
Milestone 2
- End-to-end trigger → nudge loop (zero AI)
- Main process serves hardcoded image + quote per trigger/mode
- Ambient overlay on trigger with auto-dismiss
- Fullscreen overlay window — covers your entire display even when the main app is minimized
- Demo trigger buttons (CV detection lands in M1)
Try it: pnpm dev → Start session → click Slouch or Phone → overlay appears.
Pika integration is deferred to milestone 8.
Milestone 0
- Electron + React + TypeScript shell
- Live webcam feed in the renderer
- Sentry initialized in main and renderer processes
- Typed IPC bridge (
window.anchor) with stub handlers
Scripts
| Command | Description |
|---|---|
pnpm dev | Start Electron in development mode |
pnpm build | Build for production |
pnpm preview | Preview production build |
pnpm typecheck | Run TypeScript checks |
Analysis
View
Metric
- 13
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- CSSIn code
- HTMLIn code
- ReactIn code
- RedisIn code
- TypeScriptIn code
6 of 6 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
319 KB
Source files
71
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
adam-ajroudi/Aslope
83 files · 597 KB · @ 77316ce
Structure
Interface
15 files · 18%Screens, components and styles rendered to the user.
Application logic
46 files · 55%Domain rules, services and shared utilities.
+1 moreBackground jobs
10 files · 12%Work run outside a request: tasks, workers and schedules.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- TypeScript52%
- YAML34%
- Markdown9%
- CSS5%
- HTML0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
package.json
npm · 18- @anthropic-ai/sdk
- @huggingface/transformers
- @mediapipe/tasks-vision
- @sentry/electron
- dotenv
- react
- react-dom
- redis
- uuid
- +9 more
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Feature verification
'Log of Wins' dashboard (no failure tally)Verified
The dashboard shows a Log of Wins rather than a tally of failures
Claimed on Devposthigh confidencerenderer/src/components/SessionDashboard.tsx:25— Dashboard heading is literally 'Log of Wins' and renders memory.wins / postSession.wins, focus score, and coach note; no failure counter
Calibration step to capture each person's good-posture baselineVerified
Posture detection only became reliable once we added a calibration step to capture each person's good-posture baseline
Claimed on Devposthigh confidencerenderer/src/vision/poseDetector.ts:76— addCalibrationSample/finishCalibration collect noseY/faceWidth samples and build a PostureCalibration baselinerenderer/src/vision/calibration.ts:19— createPostureCalibration derives goodPostureY, badPostureY, and postureRange from the collected samples
Claude + Midjourney via Anthropic MCP connector for image generationVerified
A single Claude call reasons and drives generation, calling Midjourney directly through the Messages API MCP connector
Claimed on Devposthigh confidenceelectron/services/midjourneyMcp.ts:28— generateImageFromPrompt calls client.beta.messages.create with mcp_servers pointing at the Midjourney MCP URL and tools: mcp_toolset, extracting a cdn.midjourney.com URL from the response
Claude session prep generating personalized quote pools and Midjourney image prompts per trigger/modeVerified
Claude session prep via Anthropic Messages API generates personalized quote pools and Midjourney image prompts per trigger/mode, cached in Redis, with in-memory/hardcoded fallback
Claimed on readmehigh confidenceelectron/pipelines/sessionPrep.ts:36— runSessionPrep calls Claude with PREP_SYSTEM_PROMPT requesting quotes and image prompts per trigger, falling back to getHardcodedPrep on missing key or parse failureelectron/pipelines/assetCache.ts:162— prep.imagePrompts stored to Redis under prepPrompts key for later Midjourney use
Claude-driven post-session reasoning (focus score, coach note, wins, next-session tuning)Verified
After each session, the system reasons about what happened and tunes the next session's nudges/tasks
Claimed on Devposthigh confidenceelectron/pipelines/postSession.ts:83— Calls Anthropic Messages API with the session event log and prior memory, parses focusScore/coachNote/wins/coachingAdjustments/nextTaskSuggestionelectron/pipelines/sessionPrep.ts:44— runSessionPrep feeds getMemoryContextForPrep (prior stats/insights) into the next session's Claude prep call
Deepgram voice coach reading nudges aloudVerified
Deepgram is the voice coach, reading the nudge aloud
Claimed on Devposthigh confidenceelectron/services/deepgram.ts:12— synthesizeSpeechToFile calls the Deepgram /v1/speak TTS API and writes the resulting audio to disk
Demo trigger buttons for slouch/phone (no-camera fallback)Verified
Demo trigger buttons; end-to-end trigger to nudge loop
Claimed on readmemedium confidencerenderer/src/components/WebcamFeed.tsx:179— 'Use demo feed' UI path exists for running without camera access, matching the README's WSL demo-feed instructionsshared/types.ts:110— sendTrigger IPC method lets the renderer manually fire a trigger, matching the described demo trigger buttons
Electron + React + TypeScript app shell with live webcam feed and typed IPC bridgeVerified
Electron + React + TypeScript shell, live webcam feed, typed IPC bridge (window.anchor)
Claimed on readmehigh confidencerenderer/src/components/WebcamFeed.tsx:7— CameraStatus type and getUserMedia-driven webcam UI with demo-feed fallbackshared/types.ts:101— AnchorAPI type defines the typed window.anchor IPC bridge referenced by the README
Fallback to SVG placeholders / pre-baked image stock when Midjourney failsVerified
Cache able to serve pre-baked images so the demo never depended on a live call; falls back to SVG placeholders if Midjourney fails
Claimed on readmehigh confidenceelectron/services/nudgeHardcoded.ts:3— PLACEHOLDER_IMAGES maps trigger types to nudges/*.svg files used as fallback assetselectron/pipelines/assetCache.ts:79— pickImagePath falls back to getPlaceholderImagePath when no generated images exist in the library
Fullscreen overlay window for nudge delivery, works even when main app minimizedVerified
Fullscreen overlay window covers your entire display even when the main app is minimized
Claimed on readmemedium confidenceelectron/overlayWindow.ts:1— Dedicated overlayWindow module builds a separate always-on-top overlay BrowserWindow distinct from the main app window
Instant personalized nudge delivery (image or spoken line) on triggerVerified
Delivers a personalized nudge generated in advance: an image or a short spoken line
Claimed on Devposthigh confidenceelectron/services/nudgeModality.ts:10— applyModality attaches imagePath or audioPath to the nudge payload based on rotating modality (quote/image/voice)electron/pipelines/assetCache.ts:187— serveNudge reads a pre-populated coach-line/asset library and serves quote+image+audio for a trigger
Persistence thresholds and cooldown before nudge firesVerified
Persistence thresholds and a cooldown so the system does not fire constantly
Claimed on Devposthigh confidencerenderer/src/vision/triggerEngine.ts:35— evaluate() requires debounce time elapsed (slouchDebounceMs/phoneDebounceMs) plus a separate nudgeCooldownMs before firing a trigger
Phone pickup detection via webcam using YOLO object detectionVerified
Catches you grabbing/picking up your phone
Claimed on Devposthigh confidencerenderer/src/vision/phoneDetector.ts:44— Loads onnx-community/yolo26n-ONNX model and runs detection for the phone class with confidence thresholdsrenderer/src/vision/phoneDetector.ts:179— updatePhoneState implements pickup/putdown consecutive-frame state machine
Redis-backed asset cache seeded at session start, trigger reads served from cache, with hardcoded fallback if Redis unreachableVerified
Redis asset cache, session start seeds nudges, triggers read from cache; falls back to hardcoded nudges if REDIS_URL missing or unreachable
Claimed on readmehigh confidenceelectron/pipelines/assetCache.ts:167— seedSessionCache writes placeholder assets to Redis at session startelectron/pipelines/assetCache.ts:210— serveNudge falls back to getHardcodedNudge when no cached coach line exists (e.g. Redis down)electron/services/redis.ts:74— getRedis returns null on connection failure so callers gracefully degrade to memory/hardcoded fallbacks
Sentry initialized in main and renderer processesVerified
Sentry initialized in main and renderer processes
Claimed on readmehigh confidenceelectron/services/sentry.ts:7— initSentryMain configures Sentry.init for the main processrenderer/src/main.tsx:1— main.tsx imports Sentry, matching renderer-process initialization claim
Slouch/posture detection via webcam using MediaPipe pose + face landmarksVerified
Catches you slouching using the laptop camera
Claimed on Devposthigh confidencerenderer/src/vision/poseDetector.ts:107— evaluatePosture computes slouch severity from nose-Y deviation and forward-head face-width ratio using MediaPipe PoseLandmarker/FaceDetectorrenderer/src/vision/calibration.ts:13— createPostureCalibration builds a good-posture baseline from calibration samples, matching the claimed calibration step
Redis as cross-session agent memory with vector search for semantic recall of past winsCode-supported
Redis is both the fast-clock cache and cross-session agent memory, using vector search so the coach can semantically recall past wins
Claimed on Devpostlow confidenceelectron/services/agentMemory.ts:78— loadMemorySnippets/appendMemorySnippets use plain Redis list operations (lRange/lPush/lTrim), not any vector/embedding index; no FT.SEARCH, FT.CREATE, or embedding calls exist anywhere in the codebase, so recall is recency-based, not semantic vector search
Sentry wraps generation jobs, CV loop, and IPC layerCode-supported
Sentry wraps the long-running generation jobs, the CV loop, and the IPC layer
Claimed on Devpostmedium confidenceelectron/services/sentry.ts:7— Sentry.init is configured in the main process with a beforeSend hook that triggers a reverse nudge on errorselectron/main.ts:1— electron/main.ts imports Sentry alongside app bootstrapping, but explicit wrapping of the CV loop specifically was not found via targeted search
Two-clock architecture: slow reasoning/generation clock separate from fast real-time detection/delivery clock, bridged by RedisCode-supported
The system runs on two clocks; Redis bridges them, with a latency budget under 300ms from trigger to on-screen nudge
Claimed on Devpostmedium confidenceelectron/pipelines/assetCache.ts:101— writePrepToCache/seedSessionCache pre-populate Redis during prep, and serveNudge only reads from the in-memory/Redis-backed cache at trigger time, matching the slow/fast clock splitrenderer/src/hooks/useVisionMonitor.ts:1— No explicit 300ms latency budget or timing instrumentation found in the detection loop; the sub-300ms number is not verifiable in code
Reward mode vs Consequence mode framing selectable per userClaimed only
Each user picks a framing that fits them: Reward mode shows aspirational imagery, Consequence mode shows long-term cost
Claimed on Devpostmedium confidence
An AI agent derived these features from the project’s Devpost page and readme, then searched the code for each one. Verified features are backed by cited code; claimed-only features had no supporting code, which is not by itself proof a feature is missing.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.