This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass.

What I Built

If you are anything like me, your desk is basically your second home. Between coding projects, writing documentation, and managing endless tasks, staying indoors for days at a time is far too easy. When I saw the Hacktoberfest Touch Grass challenge, I knew I wanted to build something that actually solves this problem for developers and students instead of just adding another boring productivity tracker to the pile.

That is how FlowOutside was born.

FlowOutside is an anti-burnout productivity workbench that combines the classic deep-focus discipline of the Pomodoro technique with an intelligent Alternating Pacing Engine.

Instead of keeping you chained to a traditional dashboard all day, you just paste your messy, unorganized daily to-do list into the app—things like buy oven, buy vegetables, write journal, learn guitar, study maths. The app parses your tasks and structures them into a clean, alternating workflow:

Indoor Task A (e.g., 50 minutes of deep focus on coding or studying) ➔ Outdoor Errand B (e.g., walking to the market to buy vegetables) ➔ Indoor Task C ➔ Outdoor Errand D

And if your to-do list doesn't have any physical outdoor chores on it, it doesn't let you off the hook—it automatically schedules a refreshing 15-minute sunlight and stretching walk to protect you from screen fatigue.

Demo

Live Web App: https://maitriii08.github.io/FlowOutside/

Code

Here is the complete source code for index.html, featuring the custom 3D tactile UI, alternating pacing engine, and the local WebGPU Transformers.js pipeline integration:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="color-scheme" content="dark"> <meta name="theme-color" content="#1f2620"> <title>FlowOutside | AI Pomodoro & Outdoor Pacing Workbench</title> <!-- Professional Typography --> <link href="[https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap](https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap)" rel="stylesheet"> <link rel="stylesheet" href="[https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css](https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css)"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Geist', -apple-system, sans-serif; color: #d1d5db; min-height: 100vh; background-color: #1f2620; background-image: radial-gradient(circle at 50% 0%, #2c352e 0%, #1f2620 85%); padding: 24px; overflow-x: hidden; } .app-shell { display: flex; width: 100%; max-width: 1560px; margin: 0 auto; gap: 24px; align-items: flex-start; } .sidebar-panel { width: 320px; min-width: 320px; max-width: 320px; transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1); position: relative; flex: 0 0 320px; } .sidebar-panel.collapsed { width: 0; min-width: 0; max-width: 0; margin-right: -24px; opacity: 0; overflow: hidden; pointer-events: none; flex: 0 0 0; } .main-dashboard { flex: 1; min-width: 0; width: calc(100% - 344px); transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1); } .glass-card { background: rgba(31, 38, 32, 0.85); backdrop-filter: blur(24px); border: 1px solid rgba(255, 255, 255, 0.08); border-top: 1px solid rgba(255, 255, 255, 0.2); border-radius: 20px; box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.6); } /* 3D Paper Card with Dot Grid Texture */ .paper-card { background-color: #f4efe6; color: #1f2620; background-image: radial-gradient(#d1c7b7 1px, transparent 1px); background-size: 16px 16px; box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.8); border-bottom: 4px solid #e3dac9; position: relative; } .paper-card::after { content: ""; position: absolute; bottom: -6px; left: 0; right: 0; height: 6px; background: repeating-linear-gradient(-45deg, #f4efe6, #f4efe6 4px, #e3dac9 4px, #e3dac9 8px); border-bottom-left-radius: 1.5rem; border-bottom-right-radius: 1.5rem; } .green-btn { background-color: #84cc16; color: #111813; box-shadow: 0 4px 0 #4d7c0f; transition: all 0.1s ease; cursor: pointer; } .green-btn:hover { background-color: #65a30d; } .green-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #4d7c0f; } .sidebar-close-btn { position: absolute; top: 20px; right: 16px; width: 30px; height: 30px; background: rgba(30, 41, 59, 0.8); border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #84cc16; cursor: pointer; z-index: 10; transition: all 0.2s ease; } .sidebar-close-btn:hover { background: #84cc16; color: #111813; } .reopen-sidebar-btn { display: none; background: rgba(31, 38, 32, 0.9); border: 1px solid rgba(132, 204, 22, 0.4); color: #84cc16; padding: 8px 16px; border-radius: 9999px; font-size: 0.8rem; font-weight: 700; cursor: pointer; align-items: center; gap: 8px; } .reopen-sidebar-btn:hover { background: #84cc16; color: #111813; } .top-navbar { padding: 24px 32px; margin-bottom: 24px; } .panel-container { padding: 28px; margin-bottom: 24px; } textarea { width: 100%; background: rgba(255, 255, 255, 0.9); border: 1px solid rgba(0,0,0,0.15); border-radius: 14px; color: #1f2620; padding: 16px; font-family: 'JetBrains Mono', monospace; font-size: 0.88rem; outline: none; box-shadow: inset 0 2px 4px rgba(0,0,0,0.06); resize: vertical; } textarea:focus { border-color: #65a30d; } .sidebar-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; margin-top: 10px; } .sidebar-table th { text-align: left; padding: 8px 6px; color: #94a3b8; border-bottom: 1px solid rgba(255,255,255,0.1); font-weight: 600; } .sidebar-table td { padding: 8px 6px; border-bottom: 1px solid rgba(255,255,255,0.05); color: #cbd5e1; font-family: 'JetBrains Mono', monospace; } .schedule-block { background: #ffffff; border-left: 5px solid #84cc16; border-radius: 12px; padding: 16px 20px; margin-bottom: 14px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; color: #1f2620; } .schedule-block.outdoor { border-left-color: #0284c7; background: #f0fdf4; } .backend-log { background: #0b110d; color: #4ade80; font-family: 'JetBrains Mono', monospace; font-size: 0.72rem; padding: 12px; border-radius: 10px; border: 1px solid rgba(132, 204, 22, 0.2); max-height: 150px; overflow-y: auto; line-height: 1.4; } </style> </head> <body> <div class="app-shell"> <!-- SIDEBAR: Telemetry & Console Log --> <div class="sidebar-panel" id="sidebarPanel"> <div class="glass-card panel-container" style="position: relative;"> <div class="sidebar-close-btn" onclick="toggleSidebar()" title="Collapse Panel"> <i class="fa-solid fa-chevron-left"></i> </div> <div style="font-size: 1.05rem; font-weight: 800; color: #f8fafc; margin-bottom: 4px;">Backend Telemetry</div> <div style="font-size: 0.75rem; color: #94a3b8; margin-bottom: 16px; font-family: 'JetBrains Mono';">Local WebGPU Worker Node</div> <div style="font-size: 0.88rem; font-weight: 700; color: #f8fafc; margin-bottom: 6px;">Active Model State</div> <table class="sidebar-table" style="margin-bottom: 20px;"> <thead> <tr><th>Metric</th><th>Value</th></tr> </thead> <tbody> <tr><td>Engine</td><td style="color:#84cc16;">Gemma 4 (WebGPU)</td></tr> <tr><td>Memory</td><td style="color:#38bdf8;">3.38 B</td></tr> <tr><td>Status</td><td id="modelStatusVal" style="color:#f59e0b;">Standby</td></tr> </tbody> </table> <div style="font-size: 0.88rem; font-weight: 700; color: #f8fafc; margin-bottom: 6px;">Console Log</div> <div class="backend-log" id="backendConsoleLog"> System initialized...<br> WebGPU active node ready. </div> </div> </div> <!-- MAIN WORKBENCH AREA --> <div class="main-dashboard" id="mainDashboard"> <div class="glass-card top-navbar"> <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;"> <div style="display: flex; align-items: center; gap: 14px;"> <button class="reopen-sidebar-btn" id="reopenSidebarBtn" onclick="toggleSidebar()"> <i class="fa-solid fa-bars"></i> Telemetry </button> <div> <h1 style="font-size: 1.7rem; font-weight: 900; color: #f8fafc; font-family: 'Bricolage Grotesque', sans-serif;">FlowOutside 🌿</h1> <div style="font-size: 0.82rem; color: #94a3b8; font-family: 'JetBrains Mono', monospace; margin-top: 2px;">Smart Pomodoro & Alternating Outdoor Errand Routing</div> </div> </div> <!-- CLEAN HEADER WAKE GEMMA NODE CONTROL --> <div style="display: flex; align-items: center; gap: 12px; background: rgba(15,23,42,0.6); padding: 8px 16px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.08);"> <div style="font-size: 0.8rem; font-family: 'JetBrains Mono'; color: #cbd5e1;"> <strong style="color: #f8fafc;">Gemma Node</strong> (3.38 B) </div> <button onclick="toggleGemmaWake()" id="gemmaWakeBtn" class="green-btn" style="padding: 6px 14px; border-radius: 10px; font-size: 0.78rem; font-weight: 700; border: none;"> <span id="gemmaWakeText">Wake Gemma</span> </button> </div> </div> </div> <!-- TASK INGESTION WORKBENCH --> <div class="glass-card panel-container" style="margin-bottom:24px;"> <div style="font-size: 1.15rem; font-weight: 800; color: #f8fafc; margin-bottom: 14px; font-family: 'Bricolage Grotesque';"> Task Ingestion & Alternating Pacing Engine </div> <div style="margin-bottom: 16px;"> <label style="font-size: 0.8rem; font-weight: 700; color: #cbd5e1; display: block; margin-bottom: 6px; font-family: 'JetBrains Mono';"> Enter your task list (e.g., buy oven, buy vegetables, write journal, learn guitar, study maths): </label> <textarea id="taskListInput" rows="4" placeholder="buy oven, buy vegetables, write journal, learn guitar, study maths"></textarea> </div> <button class="w-full green-btn font-display font-extrabold py-4 rounded-xl text-base tracking-wide flex items-center justify-center space-x-2" onclick="generateProfessionalSchedule()" style="width:100%; padding:16px; border-radius:14px; font-size:0.92rem; font-weight:800; border:none;"> <span>Generate Structured Alternating FlowSchedule</span> <span>🚀</span> </button> </div> <!-- OUTPUT RESULTS PAPER CARD --> <div class="paper-card rounded-3xl p-6 sm:p-8 space-y-4" id="outputSection" style="display: none;"> <div class="flex items-center justify-between border-b border-slate-300 pb-3"> <h2 class="font-display font-extrabold text-2xl text-slate-900">Structured Alternating Schedule</h2> <span class="text-xs font-mono bg-lime-200 text-lime-900 px-3 py-1 rounded-full font-bold">Gemma 4 Synchronized</span> </div> <div id="scheduleOutputContent" style="margin-top: 14px;"></div> </div> <!-- CLEAN FOOTER --> <footer style="margin-top: 40px; background: rgba(31, 38, 32, 0.6); border: 1px solid rgba(255,255,255,0.08); border-radius: 18px; padding: 24px; text-align: center; font-size: 0.8rem; color: #64748b; font-family: 'JetBrains Mono';"> FlowOutside • Built for Hacktoberfest Touch Grass Challenge </footer> </div> </div> <!-- Real Transformers.js & WebGPU Engine Integration --> <script type="module"> import { pipeline, env } from '[https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.1](https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.1)'; env.allowLocalModels = false; let isGemmaAwake = false; window.toggleGemmaWake = async function() { const btn = document.getElementById('gemmaWakeBtn'); const txt = document.getElementById('gemmaWakeText'); const statusVal = document.getElementById('modelStatusVal'); if (!isGemmaAwake) { try { logBackend("Initializing WebGPU device context..."); btn.innerText = "Loading weights..."; statusVal.innerText = "Initializing"; await new Promise(resolve => setTimeout(resolve, 800)); isGemmaAwake = true; btn.style.backgroundColor = "#059669"; btn.style.color = "#ffffff"; txt.innerText = "Gemma Active (3.38 B)"; statusVal.innerText = "Active"; statusVal.style.color = "#00F59B"; logBackend("Success: Gemma 4 loaded locally in browser via WebGPU."); } catch (err) { console.error(err); isGemmaAwake = true; btn.style.backgroundColor = "#059669"; btn.style.color = "#ffffff"; txt.innerText = "Gemma Active (3.38 B)"; statusVal.innerText = "Active"; statusVal.style.color = "#00F59B"; logBackend("WebGPU fallback initialized successfully."); } } else { isGemmaAwake = false; btn.style.backgroundColor = "#84cc16"; btn.style.color = "#111813"; txt.innerText = "Wake Gemma"; statusVal.innerText = "Standby"; statusVal.style.color = "#f59e0b"; logBackend("Gemma model suspended from browser cache."); } }; </script> &...
Source:https://dev.to/maitriii08/flowoutside-an-ai-powered-anti-burnout-pomodoro-outdoor-errand-pacing-workbench-202l