EMOM Timer
Customizable timer for your workout!
This is a customizable EMOM (Every Minute On the Minute) timer built with Rust and Yew, featuring a drift-correcting countdown timer library that can be reused in your own WebAssembly projects.
Features
- 🎯 EMOM Timer Web App: Full-featured workout timer with visual cues
- 📚 Reusable Library: Drift-correcting
CountdownTimerfor accurate timekeeping in WASM - ⚡ Performance: Uses recursive
Timeoutwith wall clock sync to prevent drift - 🦀 Pure Rust: Built entirely in Rust, compiled to WebAssembly
Using the CountdownTimer Library
The emom crate includes a reusable CountdownTimer that provides accurate timing in WebAssembly environments:
use ;
let config = default; // 100ms ticks
let timer = new;
timer.start;
See LIBRARY_USAGE.md for detailed examples and usage with Yew, Leptos, and other frameworks.
Why This Timer Library?
Unlike JavaScript's setInterval, this timer:
- Corrects for drift: Syncs with wall clock to maintain accuracy
- Works in background tabs: Adjusts when browser throttles timers
- Precise: Maintains accuracy even under CPU load
Add to your Cargo.toml:
[]
= "1.0"
Introduction
Yew is a modern Rust framework for creating multi-threaded front-end web apps using WebAssembly. It's comparable to JavaScript frameworks like React or Vue.js, but with the performance and safety benefits of Rust. Here are the key aspects of Yew:
-
WebAssembly: Yew compiles to WebAssembly (Wasm), enabling web applications to run at near-native speed. This makes Yew a powerful choice for performance-critical web applications.
-
Component-Based: Like React and Vue, Yew uses a component-based architecture. This makes it easier to build complex interfaces, as the UI is broken down into independent, reusable components.
-
Rust Programming Language: Leveraging Rust's performance and safety features, Yew ensures memory safety and thread safety, minimizing common web development bugs like memory leaks.
-
Concurrent and Multi-Threaded: Rust's support for concurrency and Yew's design allow for multi-threaded applications. This can lead to better performance, especially on modern multi-core processors.
-
JS Interoperability: Yew can interoperate with JavaScript, allowing developers to use existing JavaScript libraries and frameworks alongside Yew.
-
Rich Tooling and Ecosystem: Yew benefits from Rust's tooling, such as Cargo for package management, and an active community contributing to its ecosystem.
-
Virtual DOM: Like React, Yew uses a virtual DOM to optimize rendering. It only updates the parts of the real DOM that have changed, leading to efficient rendering and improved performance.
-
Declarative UI: Yew embraces a declarative approach to defining UI, which can make code more readable and easier to reason about compared to imperative UI coding.
-
Macro-based Syntax: Yew uses Rust macros to provide a JSX-like syntax, making it familiar for developers coming from a React background.
-
Strong Type System: Leveraging Rust’s strong type system, Yew applications benefit from compile-time error checking, which can catch errors early in the development process.
Yew is particularly suited for applications where performance, reliability, and Rust's strong type system are important. However, it does require familiarity with Rust, and the ecosystem is not as mature as JavaScript's, which could be a consideration for some projects.
Quick Start
VSCode
Reopen in container
Build Trunk
Serve Trunk
Library
Using emom as a Reusable Timer Library
The emom crate provides a drift-correcting countdown timer that works in WebAssembly environments. Unlike simple Interval-based timers, it actively corrects for browser timing drift to ensure accurate timing over long periods.
Features
- Drift Correction: Automatically syncs with wall clock to prevent timing drift
- Configurable: Adjust tick interval, sync frequency, and correction thresholds
- Framework Agnostic: Works with Yew, Leptos, Dioxus, or vanilla WASM
- Accurate: Maintains precision even in background tabs or under CPU load
Installation
[]
= { = "https://github.com/jac18281828/emomtimer" }
Basic Usage
Simple Countdown
use ;
// Create a timer that ticks every 100ms
let config = default;
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;
With Yew
use *;
use ;
Configuration
use TimerConfig;
let config = TimerConfig ;
Configuration Guidelines
- interval_ms: The tick interval. 100ms is good for displaying tenths of seconds
- sync_interval_ticks: How often to check for drift. Every 10 ticks (1 second) works well
- sync_threshold_ticks: Minimum drift before correction. 1 tick prevents micro-corrections
Why This Approach?
JavaScript timers (setTimeout, setInterval) can drift significantly:
- Background tabs may throttle timers to 1Hz
- CPU load can delay callbacks
- Browser power-saving features affect timing
This library solves these issues by:
- Using
Timeout(setTimeout) for each tick - Tracking expected tick time based on wall clock
- Periodically syncing actual ticks with wall clock time
- Correcting drift when it exceeds threshold
This ensures your timer stays accurate even in challenging conditions.
