JSFiddle - React, Tailwind, and code Playground

JavaScript

Timer = function Timer(callback, interval) {
	var id = Timer.prototype.__idCounter++;

	Timer.prototype.__hash[id] = callback;

	this.started = false;

	this.package = JSON.stringify({
		id: id,
		interval: interval
	});
}

Timer.prototype.__workerScript = function() {
	var __hash = {};

	onmessage = function(ev) {
		var data = JSON.parse(ev.data);

		if ( __hash[data.id] === undefined ) {
			__hash[data.id] = {
				id: data.id,
				lastTime: performance.now(),
				interval: data.interval,
        step: 0
			}
		}
		else delete __hash[data.id];
	}

	function loop(start) {
		if ( !start ) for ( var id in __hash ) step(__hash[id]);

		setTimeout(loop, 0);
	}

	loop(true);

	function step(unit) {
		while ( performance.now() - unit.lastTime >= unit.interval ) {
			unit.lastTime += unit.interval;
      unit.step++;
      var now = Date.now();
      console.log("ww", unit.step, "-", now/1000);
			postMessage(JSON.stringify({id: unit.id, time: now, step: unit.step}));
		}
	}
}

Timer.prototype.__idCounter = 0;
Timer.prototype.__hash = {};

Timer.prototype.__worker = new Worker(URL.createObjectURL(new Blob(['('+Timer.prototype.__workerScript.toString()+')()'], {type: 'application/javascript'})));

Timer.prototype.__worker.onmessage = function(ev) {
	var data = JSON.parse(ev.data);
	Timer.prototype.__hash[data.id](data.time, data.step);
}

Timer.prototype.start = function(){
	if ( !this.started ) {
		Timer.prototype.__worker.postMessage(this.package);
		this.started = true;
	}
}

Timer.prototype.stop = function(){
	if ( this.started ) {
		Timer.prototype.__worker.postMessage(this.package);
		this.started = false;
	}
}

var timer = new Timer(function(wwTime, step) {
	var now = Date.now();
	console.log("m_", step, "-", wwTime/1000, now/1000, (now-wwTime)/1000);
}, 100);

runTime = Date.now();
console.log("starting in 2sec", runTime/1000);

setTimeout(function() {
	timer.start();
}, 2000);