A simple, distraction-free focus tool designed and built from scratch.
Turn focus into a physical system—not another app you forget to open.
The Problem
Focus is harder than it should be.
Between notifications, apps, and constant distractions, it’s easy to lose entire days without doing meaningful work.
I’ve tried:
- productivity apps
- timers on my phone
- different systems and techniques
And the result is always the same:
I either forget to use them… or they’re too complex to stick with.
So instead of downloading another app, I decided to build something.
The Idea
Create a physical Pomodoro timer that lives on my desk.
No screens.
No menus.
No distractions.
Just:
- one button
- one purpose
- one system
A tool that encourages focus simply by existing.
What It Does
This timer follows a standard Pomodoro cycle:
- 25-minute focus session
- 5-minute break session
Behavior
- Press the button
- Timer begins
- LED ring gradually fills over 25 minutes
- Audible chime signals the end of focus
- LEDs count back down over 5 minutes
- Final chime signals reset
That’s it.
No extra features.
No overengineering.
Just a system that works.
Components Used
Core Components
- Arduino Nano
- 24-LED ring (NeoPixel)
- Momentary push button
- Piezo buzzer
Supporting Components
- Resistor (for LED data stability)
- Capacitor (to prevent flicker / voltage spikes)
- Breadboard + jumper wires
Build Components
- 3D printed enclosure
- Laser-cut acrylic lens
- Optional base (angled stand)
Electronics Overview
The system is intentionally simple:
- Button → triggers cycle
- LED ring → visual progress indicator
- Buzzer → audio feedback
- Arduino Nano → controls logic
Key Learnings
- Capacitor placement helped eliminate LED flickering
- Shorter data lines improved signal stability
- Resistor placement near the LED ring made a noticeable difference
- Buzzer needed a resistor to smooth out tone
The Code
I approached this from a desired behavior first mindset.
Instead of writing everything from scratch, I:
- Defined exactly how I wanted it to behave
- Used ChatGPT to generate a starting point
- Iterated and debugged until it worked
Core Logic
- Idle state
- Focus cycle (increment LEDs)
- Break cycle (decrement LEDs)
- Audio cues at transitions
#include <Adafruit_NeoPixel.h>
// ================== HARDWARE ==================
#define LED_PIN 6
#define LED_COUNT 24
#define BUTTON_PIN 2
#define BUZZER_PIN 9
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// ================== DEMO MODE ==================
const bool DEMO_MODE = false; // false = real minutes
const unsigned long TICK_MS = DEMO_MODE ? 1000UL : 60000UL;
// ================== TIMING ==================
const int FOCUS_MINUTES = 25;
const int COOLDOWN_MINUTES = 5;
// ================== LED SETTINGS ==================
const uint8_t BRIGHTNESS = 40;
// ================== BUTTON DEBOUNCE ==================
bool buttonStableState = HIGH;
bool lastButtonReading = HIGH;
unsigned long lastDebounceMs = 0;
const unsigned long DEBOUNCE_MS = 30;
// ================== STATE MACHINE ==================
enum Mode { IDLE, FOCUS, COOLDOWN, COMPLETE };
Mode mode = IDLE;
unsigned long modeStartMs = 0;
// Focus state
int focusLit = 0;
// Cooldown state
int cooldownRemoved = 0;
int cooldownLit = 24;
unsigned long cooldownNextTickMs = 0;
// Complete animation
int breatheCyclesDone = 0;
bool breatheIncreasing = true;
int breatheLevel = 0;
unsigned long breatheLastStepMs = 0;
const unsigned long BREATHE_STEP_MS = 20;
const int BREATHE_CYCLES = 3;
// ================== HELPERS ==================
void clearStrip() {
strip.clear();
strip.show();
}
void flashConfirm() {
for (int i = 0; i < LED_COUNT; i++) strip.setPixelColor(i, strip.Color(255, 255, 255));
strip.show(); delay(80);
clearStrip(); delay(80);
for (int i = 0; i < LED_COUNT; i++) strip.setPixelColor(i, strip.Color(255, 255, 255));
strip.show(); delay(80);
clearStrip();
}
bool buttonPressedEvent() {
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonReading) {
lastDebounceMs = millis();
lastButtonReading = reading;
}
if ((millis() - lastDebounceMs) > DEBOUNCE_MS) {
if (reading != buttonStableState) {
buttonStableState = reading;
if (buttonStableState == LOW) return true;
}
}
return false;
}
// ================== SOUND (LOWER OCTAVE) ==================
// End of focus: gentle ascending chime (one octave lower)
void playFocusDoneJingle() {
tone(BUZZER_PIN, 494, 180); delay(230); // B4
tone(BUZZER_PIN, 587, 200); delay(250); // D5
tone(BUZZER_PIN, 784, 240); delay(290); // G5
noTone(BUZZER_PIN);
}
// End of cooldown / completion: gentle descending chime (one octave lower)
void playCompleteJingle() {
tone(BUZZER_PIN, 784, 200); delay(250); // G5
tone(BUZZER_PIN, 587, 220); delay(270); // D5
tone(BUZZER_PIN, 494, 260); delay(320); // B4
noTone(BUZZER_PIN);
}
// ================== RENDER ==================
void renderLitCount(int litCount, uint32_t color) {
strip.clear();
for (int i = 0; i < litCount; i++) strip.setPixelColor(i, color);
strip.show();
}
void renderFocus(unsigned long elapsedMs) {
int minute = (int)(elapsedMs / TICK_MS) + 1;
int targetLit;
if (minute <= 1) targetLit = 0;
else targetLit = constrain(minute - 1, 0, 24);
if (targetLit != focusLit) {
focusLit = targetLit;
renderLitCount(focusLit, strip.Color(0, 255, 0));
}
}
void renderCooldownLit() {
renderLitCount(cooldownLit, strip.Color(0, 120, 255));
}
// ================== TRANSITIONS ==================
void startFocus() {
mode = FOCUS;
modeStartMs = millis();
focusLit = 0;
clearStrip();
}
void startCooldown() {
mode = COOLDOWN;
modeStartMs = millis();
cooldownRemoved = 0;
cooldownLit = 24;
renderCooldownLit();
cooldownNextTickMs = millis() + TICK_MS;
}
void startComplete() {
mode = COMPLETE;
modeStartMs = millis();
breatheCyclesDone = 0;
breatheIncreasing = true;
breatheLevel = 0;
breatheLastStepMs = millis();
}
void backToIdle() {
mode = IDLE;
clearStrip();
}
// ================== SETUP / LOOP ==================
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
strip.begin();
strip.setBrightness(BRIGHTNESS);
strip.show();
}
void loop() {
if (buttonPressedEvent() && mode == IDLE) {
flashConfirm();
startFocus();
}
unsigned long now = millis();
if (mode == FOCUS) {
unsigned long elapsed = now - modeStartMs;
renderFocus(elapsed);
if (elapsed >= (unsigned long)FOCUS_MINUTES * TICK_MS) {
playFocusDoneJingle();
startCooldown();
}
}
if (mode == COOLDOWN) {
if (cooldownRemoved < COOLDOWN_MINUTES && now >= cooldownNextTickMs) {
cooldownRemoved++;
cooldownLit = 24 - cooldownRemoved;
renderCooldownLit();
cooldownNextTickMs += TICK_MS;
}
if (cooldownRemoved >= COOLDOWN_MINUTES) {
playCompleteJingle();
startComplete();
}
}
if (mode == COMPLETE) {
if ((now - breatheLastStepMs) >= BREATHE_STEP_MS) {
breatheLastStepMs = now;
if (breatheIncreasing) breatheLevel += 5;
else breatheLevel -= 5;
if (breatheLevel >= 255) {
breatheLevel = 255;
breatheIncreasing = false;
} else if (breatheLevel <= 0) {
breatheLevel = 0;
breatheIncreasing = true;
breatheCyclesDone++;
}
strip.clear();
uint32_t c = strip.Color(0, breatheLevel, breatheLevel);
for (int i = 0; i < 19; i++) strip.setPixelColor(i, c);
strip.show();
if (breatheCyclesDone >= BREATHE_CYCLES) {
backToIdle();
}
}
}
}
Prototyping
The first version was built on a breadboard.
Goals
- Validate logic
- Test LED behavior
- Identify electrical issues
Issues Encountered
- LED flickering
- Incorrect transition to break cycle
- Wiring inefficiencies
All solved through:
- component placement adjustments
- code tweaks
Enclosure Design (Fusion 360)
Once the electronics worked, I moved into enclosure design.
Workflow
- Imported component models (STEP files from GrabCAD)
- Arranged layout in Fusion 360
- Designed around real geometry (not guessing dimensions)
Key Design Decisions
- Separate back plate for easier assembly
- Peg alignment system for clean construction
- Front plate to hold LED ring + lens
- Internal cavities for components
👉 [OPTIONAL: INSERT FUSION SCREENSHOT OR IMAGE HERE]
3D Printing Iteration
First print revealed a few issues:
- Button hole too tight
- USB port clearance too small
- Mounting holes slightly misaligned
Adjusted and reprinted.
👉 [OPTIONAL: INSERT PROTOTYPE PHOTO HERE]
Lens + Lighting
The front lens was:
- laser cut from acrylic
- engraved for light diffusion
This helped:
- soften LED brightness
- create a cleaner visual output
👉 [OPTIONAL: INSERT IMAGE OF LENS / LIGHT DIFFUSION HERE]
Final Design Details
- Circular lens with trim ring for a more finished look
- Angled base for better visibility on a desk
- Internal layout optimized for cleaner wiring
- Terminal adapter used for easier assembly and maintenance
👉 [INSERT FINAL PRODUCT PHOTOS HERE]
Why This Matters
This isn’t just a timer.
It’s a system.
A physical object that:
- removes friction
- builds a habit
- improves focus
Instead of relying on discipline, it creates an environment that encourages it.
Files & Resources
All files for this project are available here:
👉 [INSERT PATREON / DOWNLOAD LINK HERE]
Includes:
- STL files
- Fusion 360 files
- Arduino code
What I’d Improve
- Battery-powered version
- Smaller footprint
- Multiple modes (deep work, short sessions)
- Cleaner internal wiring system
Build Your Own
If you’ve ever struggled with focus, try building your own version.
or take this idea and push it further.
That’s the whole point.
Project Details
- Build Time: 5-6 hours
- Skill Level: intermediate
- Tools Used: 3D Printer, Soldering Iron, Laser Cutter
- Materials Cost: $20-$30