Skip to content

How plugins run

Plugins are loaded when Nari starts, and users can turn them on and off at any time without restarting.

When a plugin is turned on:

  1. main.lua runs from top to bottom. This is where you register providers, connect events and so on.
  2. If a song is loaded, the track and state events fire with it right away, so a plugin turned on in the middle of a song still sees it.
  3. If a screen is open, the page event fires for it.
  4. ready fires once Nari finished starting, or right away if it already did.

main.lua runs on Godot’s main thread, the one that draws the screen. Don’t block it: use callbacks for anything slow, or move heavy work to a background thread.

When a plugin is turned off, uninstalled or updated:

  1. disable fires.
  2. Nari cleans up after the plugin:
    • lyrics providers, music providers, sound effects, visualizers, settings, Home entries and Now Playing tabs it added are removed
    • every signal connection made from Lua is disconnected
    • every node the plugin created is freed
    • its background threads are stopped, even if they are stuck in a loop
    • pending nari.http callbacks are dropped

If Nari closes unexpectedly three times in a row within 30 seconds of starting, the next session starts in safe mode with no plugins loaded. Users can still turn plugins on and off; the change applies from the next session, and F5 in developer mode leaves safe mode.

  • Code that runs on the main thread for more than 3 seconds without returning stops the plugin. See Debugging.
  • Each Lua state (main.lua and every thread) can use up to 128 MB of memory.
  • os.exit() raises an error instead of closing Nari.
  • Threads can’t touch Godot. Send a message to main.lua when something needs to change on screen.
  • Messages to main.lua wait while Android keeps the app in the background, and arrive when it comes back.
  • Values are copied between Lua and Godot. Godot’s typed arrays (Array[String]…) come back as plain arrays.
  • Global Godot enums (HORIZONTAL_ALIGNMENT_CENTER…) aren’t exposed. Use their numbers (HORIZONTAL_ALIGNMENT_CENTER is 1).