diff --git a/app/src/util/tick.c b/app/src/util/tick.c
index b85ce971..cc0bab5e 100644
--- a/app/src/util/tick.c
+++ b/app/src/util/tick.c
@@ -1,16 +1,55 @@
 #include "tick.h"
 
-#include <SDL2/SDL_timer.h>
+#include <assert.h>
+#include <time.h>
+#ifdef _WIN32
+# include <windows.h>
+#endif
 
 sc_tick
 sc_tick_now(void) {
-    // SDL_GetTicks() resolution is in milliseconds, but sc_tick are expressed
-    // in microseconds to store PTS without precision loss.
-    //
-    // As an alternative, SDL_GetPerformanceCounter() and
-    // SDL_GetPerformanceFrequency() could be used, but:
-    //  - the conversions (avoiding overflow) are expansive, since the
-    //    frequency is not known at compile time;
-    //  - in practice, we don't need more precision for now.
-    return (sc_tick) SDL_GetTicks() * 1000;
+#ifndef _WIN32
+    // Maximum sc_tick precision (microsecond)
+    struct timespec ts;
+    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret) {
+        abort();
+    }
+
+    return SC_TICK_FROM_SEC(ts.tv_sec) + SC_TICK_FROM_NS(ts.tv_nsec);
+#else
+    LARGE_INTEGER c;
+
+    // On systems that run Windows XP or later, the function will always
+    // succeed and will thus never return zero.
+    // <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
+    // <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency>
+
+    BOOL ok = QueryPerformanceCounter(&c);
+    assert(ok);
+    (void) ok;
+
+    LONGLONG counter = c.QuadPart;
+
+    static LONGLONG frequency;
+    if (!frequency) {
+        // Initialize on first call
+        LARGE_INTEGER f;
+        ok = QueryPerformanceFrequency(&f);
+        assert(ok);
+        frequency = f.QuadPart;
+        assert(frequency);
+    }
+
+    if (frequency % SC_TICK_FREQ == 0) {
+        // Expected case (typically frequency = 10000000, i.e. 100ns precision)
+        sc_tick div = frequency / SC_TICK_FREQ;
+        return SC_TICK_FROM_US(counter / div);
+    }
+
+    // Split the division to avoid overflow
+    sc_tick secs = SC_TICK_FROM_SEC(counter / frequency);
+    sc_tick subsec = SC_TICK_FREQ * (counter % frequency) / frequency;
+    return secs + subsec;
+#endif
 }
diff --git a/app/src/util/tick.h b/app/src/util/tick.h
index 47d02529..2d941f23 100644
--- a/app/src/util/tick.h
+++ b/app/src/util/tick.h
@@ -10,9 +10,11 @@ typedef int64_t sc_tick;
 #define SC_TICK_FREQ 1000000 // microsecond
 
 // To be adapted if SC_TICK_FREQ changes
+#define SC_TICK_TO_NS(tick) ((tick) * 1000)
 #define SC_TICK_TO_US(tick) (tick)
 #define SC_TICK_TO_MS(tick) ((tick) / 1000)
 #define SC_TICK_TO_SEC(tick) ((tick) / 1000000)
+#define SC_TICK_FROM_NS(ns) ((ns) / 1000)
 #define SC_TICK_FROM_US(us) (us)
 #define SC_TICK_FROM_MS(ms) ((ms) * 1000)
 #define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)