Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.19.0
Plan: Gamified Productivity Buddy
Context
Building a hackathon entry for HF "Build Small" (Thousand Token Wood track, deadline June 15 2026 β 2 days). The app is a whimsical RPG-flavored productivity companion: a 3D character lives on the left, a local LLM buddy lives on the right, and your focus sessions earn XP, coins, and cosmetic gear.
Tech Stack
| Layer | Choice | Reason |
|---|---|---|
| App framework | Gradio gr.Blocks |
Required by hackathon |
| Hosting | HF Spaces (GPU T4) | Required; enables Off-the-Grid badge |
| LLM | Qwen/Qwen2.5-3B-Instruct via π€ transformers |
3B = fast on T4, excellent tool-call support |
| 3D avatar | Three.js + GLB/GLTF model | User confirmed; served from Gradio static |
| Frontend glue | Embedded <script> in gr.HTML + hidden textbox bridge |
JSβPython event passing |
| Python env | venv |
User requirement |
Bonus badges targeted: Off the Grid (no cloud APIs), Off-Brand (custom frontend), Tiny Titan (β€4B model).
Project Structure
08/
βββ app.py # Gradio entry point
βββ src/
β βββ llm.py # Model load, chat, tool-call parsing
β βββ game_state.py # XP / coins / levels / inventory dataclass
β βββ tools.py # Tool definitions + executor
β βββ reminder.py # Idle detection thread
βββ static/
β βββ js/
β β βββ avatar.js # Three.js scene, GLB loader, animations
β β βββ timer.js # Pomodoro / free-flow countdown + JS events
β β βββ bridge.js # Dispatches JS events β hidden Gradio textbox
β βββ css/
β β βββ style.css # 2-column RPG dark theme
β βββ models/
β βββ character.glb # Free CC0 character (see note below)
βββ requirements.txt
βββ .env.example
βββ README.md
GLB model source: Use the free CC0 "Adventurer" from Kenney (kenney.nl/assets/adventurer) or any Mixamo Y-Bot export. Place the file at static/models/character.glb. Animations needed: idle, walk, celebrate.
Layout (Gradio custom HTML)
ββββββββββββββββββββββββ¬βββββββββββββββββββββββ
β 3D Avatar (Three.js)β Chat window β
β [idle / celebrate] β (gr.Chatbot) β
β β β
β XP: 120 | β Lv.2 β ββ Inventory ββ β
β π° Coins: 35 β (slides over chat β
β β when [Bag] clicked)β
β ββ Timer ββ β β
β [π
Pomodoro] β β
β [β Free Flow] β β
β [Start] [Stop] β β
β β β
β ββ To-Do ββ β β
β [ ] Task 1 β β
β [β] Task 2 β β
β [+ Add task] β β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββ
The two columns are rendered inside a single gr.HTML component that bootstraps Three.js and the timer. Gradio components (gr.Chatbot, gr.Textbox input, hidden state bridges) sit in gr.Row/gr.Column alongside.
Implementation Steps
Tracer-bullet order: each slice is a working vertical cut of the full stack.
β 1. Bootstrap
python -m venv .venv, install deps,requirements.txtβ done
2. Chat with local LLM
Goal: bare-bones two-column Gradio app where you can talk to Qwen2.5-3B-Instruct.
src/llm.py: load model withAutoModelForCausalLM+bfloat16+device_map="auto";chat(messages) -> strapp.py: minimalgr.Blockswith left placeholder column + rightgr.Chatbot+gr.Textboxinput- System prompt: whimsical RPG companion persona
- Deliverable: type a message, get a reply
3. Timer
Goal: functional Pomodoro/free-flow timer in the left column with configurable durations.
static/js/timer.js: countdown logic, work/break modes, start/stop/resetstatic/css/style.css: dark RPG theme skeleton, two-column layout- Left column rendered via
gr.HTML: timer display + mode selector (Pomodoro / Free Flow) + duration inputs + Start/Stop buttons static/js/bridge.js: JS β Python bridge via hiddengr.Textbox- Python handler: receives
timer_completeevent, lays groundwork for XP (stub for now) - Deliverable: timer counts down, rings on completion
4. LLM β Timer tool calls
Goal: LLM can start/stop the timer via natural language.
src/game_state.py: addtimer_statedict to a minimalGameStatedataclasssrc/tools.py: definestart_timerandstop_timerJSON schemas + executorsrc/llm.py: extendchat()to pass tools, parse Qwen<tool_call>tags, execute, re-call model- Inject current timer state into system prompt each turn
- Python β JS: after tool execution, return a JS command string to update timer UI
- Deliverable: "start a 25 minute focus session" β timer starts in UI
5. Todo list
Goal: functional todo list in the left column (below timer).
- Extend
GameState: addtodos: list[dict]withid,task,done gr.HTMLtodo section: renders current todos from state, checkbox to complete, delete button- Python handlers:
add_todo,complete_todo,remove_todoupdate state + re-render HTML - Inject todo list into system prompt each turn
- Deliverable: todos appear, can be checked off manually
6. LLM β Todo tool calls
Goal: LLM can manage todos via natural language.
src/tools.py: addadd_todo,complete_todo,remove_todoschemas + executors- Wire into
chat()tool loop (same pattern as timer) - On
complete_todo: award +10 XP (introduce XP toGameStatehere) - Deliverable: "add a todo: review PR" β todo appears; "mark it done" β checked off, XP ticks up
β 7. 3D Avatar
Goal: Three.js GLB character in the left column that reacts to app events.
static/js/avatar.js: Three.js scene,GLTFLoader, animation mixer- Animations mapped to events:
idle(default),working(timer running),celebrate(timer complete / level up),cheer(todo complete) bridge.js: extend Python β JS channel to accept{cmd: "avatar", animation: "celebrate"}- Hook avatar triggers into existing events: timer complete, todo complete, LLM tool responses
- Deliverable: character idles, animates when timer runs, celebrates on completion
Gamification Values
| Event | XP | Coins |
|---|---|---|
| Complete a to-do | +10 | β |
| Finish a Pomodoro | +25 | +5 |
| Finish a free-flow session | +15 | +3 |
| Level up | β | +20 bonus |
Level thresholds: 0 / 100 / 250 / 500 / 900 / 1400 XP
Sample Inventory Items
| Item | Cost | Level Req |
|---|---|---|
| Red Hat | 10 coins | Lv.1 |
| Blue Cape | 25 coins | Lv.2 |
| Gold Crown | 50 coins | Lv.3 |
| Dark Aura | 80 coins | Lv.4 |
Verification
- Start app:
python app.pyβ opens atlocalhost:7860 - Timer: Click Start Pomodoro β countdown runs in UI β on complete, XP/coins update, avatar plays celebrate animation
- LLM tool use: Type "add a todo: finish the slides" β LLM calls
add_todo, todo appears in left column - Idle reminder: Set idle threshold to 2 min for testing, wait β nudge appears in chat
- Inventory: Click π β panel slides over chat β buy Red Hat β avatar updates
- Deploy: Push to HF Spaces, confirm GPU Space, verify model loads and chat works