EMOM Timer
A high-performance EMOM (Every Minute On the Minute) workout timer with liquid glass aesthetics, built entirely in Rust and WebAssembly.
Features
🎯 Full-Featured Workout Timer
- Precise EMOM timing with visual and color cues
- Customizable rounds and intervals
- Beautiful liquid glass UI with animated wavy cloud effects
- Responsive design optimized for all devices
- Zero drift - maintains accuracy over long sessions
📚 Reusable Countdown Timer Library
- Drift-correcting algorithm - syncs with wall clock to prevent timing errors
- Framework agnostic - works with Yew, Leptos, Dioxus, or vanilla WASM
- Production-ready - extensively tested with comprehensive test suite
- Easy to integrate - simple API with sensible defaults
Why This Timer?
Traditional JavaScript timers (setInterval, setTimeout) suffer from significant drift, especially in:
- Background tabs where browsers throttle to 1Hz
- High CPU load situations that delay callbacks
- Power-saving modes that affect timing precision
The emom countdown timer solves these problems by:
- Using recursive
Timeoutcalls for flexibility - Tracking expected tick time against wall clock
- Periodically syncing and correcting drift
- Adjusting when drift exceeds configurable thresholds
Result: Accurate timing that stays precise over minutes or hours, even under adverse conditions.
Quick Start
Use the Web App
Visit the live deployment: https://emomtimer.2ad.com
Run Locally with Docker
The easiest way to run locally is using the provided dev container:
- Open in VS Code:
Reopen in Container - Build:
trunk build --release - Serve:
trunk serve --address=0.0.0.0 --release - Open your browser to
http://localhost:8080
Using the Countdown Timer Library
Add to your Cargo.toml:
[]
= { = "https://github.com/jac18281828/emomtimer" }
Basic Example
use ;
let config = default; // 100ms ticks
let timer = new;
timer.start;
// ... later ...
timer.stop;
Countdown from Duration
use ;
use RefCell;
use Rc;
let remaining = new; // 60 seconds in tenths
let remaining_clone = clone;
let timer = new;
timer.start;
Integration with Yew
use *;
use ;
use Rc;
Configuration
Customize the timer behavior:
use TimerConfig;
let config = TimerConfig ;
Configuration Guidelines:
interval_ms: Tick interval in milliseconds. Use 100 for tenths of seconds, 1000 for full secondssync_interval_ticks: How often to check for drift. Every 10 ticks (1 second) is recommendedsync_threshold_ticks: Minimum drift before correction. Set to 1 to prevent micro-corrections
See LIBRARY_USAGE.md for detailed examples and advanced usage patterns.
Technology Stack
Built with modern Rust tooling and frameworks:
- Rust - Systems programming language ensuring memory safety and performance
- Yew - Modern Rust framework for building WebAssembly web applications
- WebAssembly - Near-native performance in the browser
- Trunk - WASM web application bundler
- gloo-timers - Thin Rust wrapper over browser timing APIs
Why Yew and WebAssembly?
Yew is a modern Rust framework comparable to React or Vue.js, but with unique advantages:
- WebAssembly Performance: Compiles to WASM for near-native execution speed
- Component-Based Architecture: Build complex UIs with reusable, isolated components
- Memory Safety: Leverage Rust's guarantees to eliminate memory leaks and data races
- Strong Type System: Catch errors at compile time, not runtime
- Virtual DOM: Efficient rendering with minimal DOM updates
- Declarative UI: Clear, readable code with macro-based JSX-like syntax
- JavaScript Interoperability: Use existing JS libraries when needed
- Rich Tooling: Cargo for package management, excellent IDE support
Perfect for: Applications where performance, reliability, and type safety are critical.
Development
Prerequisites
- Docker (for dev container)
- OR: Rust 1.70+, trunk, wasm-bindgen
Building
# Development build
# Release build with optimizations
Testing
# Run all tests
# Run with coverage
# Lint and format
Project Structure
emomtimer/
├── src/
│ ├── lib.rs # Library exports and countdown timer
│ ├── main.rs # Yew application and UI
│ └── countdown_timer.rs # Drift-correcting timer implementation
├── style.css # Liquid glass UI styling
├── index.html # Application shell
├── Cargo.toml # Dependencies and package metadata
└── README.md # This file
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Ensure
cargo testandcargo clippypass - Submit a pull request
License
This project is open source. See the repository for license details.
Acknowledgments
Built with ❤️ using Rust and WebAssembly. Special thanks to the Yew and Rust communities for excellent tooling and documentation.
