<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl" -->

---
title: Slint and the Node.js Event Loop | daily.dev
description: Slint 1.17 fixes a long-standing inefficiency in its Node.js binding where the UI thread polled every 16 ms regardless of activity, wasting CPU and adding...
canonical: https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Slint and the Node.js Event Loop | daily.dev
og:description: Slint 1.17 fixes a long-standing inefficiency in its Node.js binding where the UI thread polled every 16 ms regardless of activity, wasting CPU and adding...
og:url: https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl
og:image: https://api.daily.dev/og/posts/3aySkjayl.png
og:image:alt: Slint and the Node.js Event Loop
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Slint and the Node.js Event Loop

**[Slint](https://daily.dev/sources/slint)** · 8 min read · 78 upvotes · 1 comments

## Summary

Slint 1.17 fixes a long-standing inefficiency in its Node.js binding where the UI thread polled every 16 ms regardless of activity, wasting CPU and adding input latency. The post explains how two event loops (Node's libuv and Slint's winit-based loop) must share a single thread, then details the solution: installing a libuv prepare hook that lets Slint block for exactly as long as libuv would sleep, and watching libuv's backend file descriptor to wake Slint when I/O is ready. This eliminates idle CPU usage and input lag on Linux and macOS. Windows remains on the 16 ms tick due to I/O completion ports not being exposed in libuv's public API, and Deno/Bun lack the necessary hooks entirely — both are planned for future work.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://slint.dev/blog/slint-and-the-nodejs-event-loop.html>

## Questions this post answers

### How does Slint 1.17 fix the 16ms event loop tick problem in its Node.js binding?

Slint 1.17 replaces the setInterval-based 16ms tick with a real libuv integration on Linux and macOS, using libuv's uv_prepare_t hook to drain Slint's event loop right before libuv's I/O poll step. It computes the sleep timeout from uv_backend_timeout(), watches libuv's backend fd via async-io to detect I/O readiness, and uses uv_async_send to hand control back to libuv, so idle apps sleep and CPU usage drops to zero when nothing's happening.

_daily.dev surfaces engineering writeups like this for developers optimizing event loop performance across runtimes._

### Does the Slint Node.js binding support Windows, Deno, and Bun with the same low-latency event loop as Linux and macOS?

No, Windows, Deno, and Bun still use the older 16ms polling tick rather than the libuv prepare-hook integration. Windows lacks a public libuv API exposing its I/O completion port handle, which Electron works around only via an unofficial patch; Deno and Bun don't use libuv at all, so the plan there is to invert loop ownership so their runtimes schedule onto Slint's loop via spawn_local.

_developers picking a JS runtime for desktop UI work can track platform-specific gaps like this through daily.dev._

### Why do Node.js and Slint's windowing backend have to share a single thread instead of running on separate threads?

Slint properties are accessed both during rendering and from GUI event callbacks that call into JavaScript, which must execute on Node's main thread, so a separate worker thread for Slint's loop isn't viable. Additionally, on macOS the GUI can only be driven from the main thread, which Node already occupies, ruling out moving Slint's winit-based loop to a worker thread.

_daily.dev helps developers debugging cross-thread GUI and event loop constraints find concrete engineering explanations._

## Community discussion

Top comments from developers on daily.dev.

**@jenueldev** · 3 upvotes

> Great breakdown of bridging two event loops. Eliminating the 16 ms polling tax is a meaningful win for idle CPU and input latency.

## Similar posts on daily.dev

- [Stop Calling Node.js “Single-Threaded”: A Look Under the Hood](https://daily.dev/posts/stop-calling-node-js-single-threaded-a-look-under-the-hood-ofsmhbjmm) · Medium · 6 upvotes · 0 comments
- [Slint 1.18 Released](https://daily.dev/posts/slint-1-18-released-qsjr785iv) · Slint · 36 upvotes · 1 comments
- [JavaScript Event Loop Explained: Queue Tracing and Debugging Guide](https://daily.dev/posts/javascript-event-loop-explained-queue-tracing-and-debugging-guide-sa0thbcrm) · SitePoint · 97 upvotes · 3 comments
- [Node.js Thread Pools on Busy Machines](https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew) · Platformatic · 49 upvotes · 4 comments
- [How the JavaScript Runtime Schedules Callbacks: A Look at the Event Loop](https://daily.dev/posts/how-the-javascript-runtime-schedules-callbacks-a-look-at-the-event-loop-qkr1ufg7o) · Medium · 4 upvotes · 0 comments

---

Tags: [#nodejs](https://daily.dev/tags/nodejs), [#rust](https://daily.dev/tags/rust)

[View this post on daily.dev](https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Slint and the Node.js Event Loop","url":"https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl"},"datePublished":"2026-07-12T07:19:51.578Z","dateModified":"2026-09-14T07:01:38.240Z","description":"Slint 1.17 fixes a long-standing inefficiency in its Node.js binding where the UI thread polled every 16 ms regardless of activity, wasting CPU and adding...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/35048dc4e9b11e0adca4df4309adb403?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/35048dc4e9b11e0adca4df4309adb403?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Slint","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Slint","logo":"https://media.daily.dev/image/upload/s--kMN8T1sT--/c_limit,w_256/f_auto,q_auto/v1783840780/logos/slint?_a=BAMAMicg0","url":"https://daily.dev/sources/slint"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":78},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"nodejs,rust","timeRequired":"PT8M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Slint","item":"https://daily.dev/sources/slint"},{"@type":"ListItem","position":3,"name":"Slint and the Node.js Event Loop"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl","comment":[{"@type":"Comment","text":"Great breakdown of bridging two event loops. Eliminating the 16 ms polling tax is a meaningful win for idle CPU and input latency.","datePublished":"2026-07-14T01:56:25.299Z","url":"https://daily.dev/posts/3aySkjayl#c-GevHJgg26","author":{"@type":"Person","name":"Jenuel Oras Ganawed","url":"https://daily.dev/jenueldev","image":"https://media.daily.dev/image/upload/s--BadmiNyj--/f_auto/v1787625077/avatars/avatar_JJtZr4Sjm?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl#faq","mainEntity":[{"@type":"Question","name":"How does Slint 1.17 fix the 16ms event loop tick problem in its Node.js binding?","acceptedAnswer":{"@type":"Answer","text":"Slint 1.17 replaces the setInterval-based 16ms tick with a real libuv integration on Linux and macOS, using libuv's uv_prepare_t hook to drain Slint's event loop right before libuv's I/O poll step. It computes the sleep timeout from uv_backend_timeout(), watches libuv's backend fd via async-io to detect I/O readiness, and uses uv_async_send to hand control back to libuv, so idle apps sleep and CPU usage drops to zero when nothing's happening. daily.dev surfaces engineering writeups like this for developers optimizing event loop performance across runtimes."}},{"@type":"Question","name":"Does the Slint Node.js binding support Windows, Deno, and Bun with the same low-latency event loop as Linux and macOS?","acceptedAnswer":{"@type":"Answer","text":"No, Windows, Deno, and Bun still use the older 16ms polling tick rather than the libuv prepare-hook integration. Windows lacks a public libuv API exposing its I/O completion port handle, which Electron works around only via an unofficial patch; Deno and Bun don't use libuv at all, so the plan there is to invert loop ownership so their runtimes schedule onto Slint's loop via spawn_local. developers picking a JS runtime for desktop UI work can track platform-specific gaps like this through daily.dev."}},{"@type":"Question","name":"Why do Node.js and Slint's windowing backend have to share a single thread instead of running on separate threads?","acceptedAnswer":{"@type":"Answer","text":"Slint properties are accessed both during rendering and from GUI event callbacks that call into JavaScript, which must execute on Node's main thread, so a separate worker thread for Slint's loop isn't viable. Additionally, on macOS the GUI can only be driven from the main thread, which Node already occupies, ruling out moving Slint's winit-based loop to a worker thread. daily.dev helps developers debugging cross-thread GUI and event loop constraints find concrete engineering explanations."}}]}
```

