Fix Focus App High CPU Usage on macOS
Problem
The Focus app (com.ftustudio.focustime) - a Pomodoro timer - consumes ~50% CPU constantly, even when idle.
Root Cause
Stack sampling revealed the app redraws its menu bar icon at an excessive rate (~60 times/second) instead of once per second:
414 samples: -[NSStatusItem _updateReplicantsUnlessMenuIsTracking:]
400 samples: -[NSStatusItem _updateReplicant:]
218 samples: -[NSStatusItem _redrawReplicantSnapshot:sourceView:]
Each tick triggers expensive bitmap capture, CALayer rendering, and compositing for a simple countdown timer that only needs 1 update/second.
Solution
Since the app is sandboxed with hardened runtime (no dylib injection possible) and the developer is unresponsive, we use a duty-cycle throttler that pauses/resumes the process.
Install
# Create throttle script
mkdir -p ~/.local/bin
cat > ~/.local/bin/throttle-focus << 'EOF'
#!/usr/bin/env python3
"""Throttle Focus app to reduce CPU usage using duty-cycle approach."""
import subprocess
import signal
import sys
import time
TARGET = "Focus"
RUN_SEC = 0.05 # Run for 50ms
PAUSE_SEC = 0.95 # Pause for 950ms (~5% duty cycle)
def get_pid():
try:
result = subprocess.run(["pgrep", "-x", TARGET], capture_output=True, text=True)
pids = result.stdout.strip().split('\n')
return int(pids[0]) if pids[0] else None
except:
return None
def send_signal(pid, sig):
try:
subprocess.run(["kill", f"-{sig}", str(pid)], capture_output=True)
except:
pass
current_pid = None
def cleanup(signum=None, frame=None):
if current_pid:
send_signal(current_pid, "CONT")
print("\nThrottling stopped.")
sys.exit(0)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
print(f"Throttling {TARGET} (run {RUN_SEC*1000:.0f}ms / pause {PAUSE_SEC*1000:.0f}ms)")
print("Press Ctrl+C to stop")
while True:
pid = get_pid()
if not pid:
print(f"Waiting for {TARGET} to start...")
time.sleep(2)
continue
current_pid = pid
send_signal(pid, "CONT")
time.sleep(RUN_SEC)
send_signal(pid, "STOP")
time.sleep(PAUSE_SEC)
EOF
chmod +x ~/.local/bin/throttle-focus
# Create Launch Agent for auto-start
cat > ~/Library/LaunchAgents/com.local.throttle-focus.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.local.throttle-focus</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/Users/hao/.local/bin/throttle-focus</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/throttle-focus.log</string>
<key>StandardErrorPath</key>
<string>/tmp/throttle-focus.log</string>
</dict>
</plist>
EOF
# Load the agent
launchctl load ~/Library/LaunchAgents/com.local.throttle-focus.plist
Management Commands
# Stop throttling
launchctl unload ~/Library/LaunchAgents/com.local.throttle-focus.plist
# Resume throttling
launchctl load ~/Library/LaunchAgents/com.local.throttle-focus.plist
# Check status
launchctl list | grep throttle
# View logs
cat /tmp/throttle-focus.log
# Uninstall completely
launchctl unload ~/Library/LaunchAgents/com.local.throttle-focus.plist
rm ~/Library/LaunchAgents/com.local.throttle-focus.plist
rm ~/.local/bin/throttle-focus
Results
| Metric | Before | After |
|---|---|---|
| CPU Usage | ~48% | ~2% |
| Menu bar updates | ~60/sec | 1/sec |
The timer still functions correctly - it just updates once per second instead of continuously.