Feather is an extensible debug tool for LÖVE projects, inspired by LoveBird. It lets you inspect logs, variables, performance metrics, and errors in real-time over a WebSocket connection and local log files, perfect for debugging games.
- 📜 Live log viewer — See
print()output instantly in the app. - 🔍 Variable inspection — Watch values update in real-time.
- 🚨 Error capturing — Automatically catch and display errors with optional delivery delay.
- 📸 Screenshots & GIF capture — Capture screenshots and record GIFs from your game via the built-in plugin.
- 🔌 Plugin system — 18 built-in plugins + custom ones. Server-driven UI: plugins define their actions in Lua, the desktop renders them automatically.
- 📱 Multi-session support — Connect multiple games simultaneously, each gets its own session tab.
- 📲 Mobile debugging — Auto-detected local IP in Settings with copyable connection string for WiFi debugging.
- 💻 Console / REPL — Optional plugin to execute Lua code in the running game. Must be explicitly included and guarded by
apiKey. - 📁 Log file viewer — Open
.featherlogfiles manually for offline inspection (great for testing and later verification). - ⚡ Zero-config setup —
require("feather.auto")registers all available plugins with sensible defaults. - 📦 One-line installer —
curl | bashscript to download core + plugins or later download plugins on demand.
This is the simplest approach, no package manager required.
- Go to the releases page and download
feather-x.x.x.zip - Unzip it and copy the
feather/folder into your project, e.g.lib/feather/ - Require it by path:
local Feather = require "lib.feather"Download the Feather library and all plugins with a single command:
curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-feather.sh | bashThis creates a feather/ directory (core library) and a plugins/ directory (all built-in plugins) in your current folder.
Customize with environment variables:
# Install into a custom directory
FEATHER_DIR=lib/feather bash -c "$(curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-feather.sh)"
# Download from a specific branch or tag
FEATHER_BRANCH=v0.6.0 bash -c "$(curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-feather.sh)"
# Skip certain plugins
FEATHER_SKIP_PLUGINS="network-inspector,memory-snapshot" bash -c "$(curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-feather.sh)"
# Skip all plugins (core only)
FEATHER_PLUGINS=0 bash -c "$(curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-feather.sh)"Install globally and use luarocks-loader to resolve the path automatically:
luarocks install featherThen at the top of your main.lua, before any require calls:
require("luarocks.loader")
local Feather = require("feather")Or install into a local tree to keep dependencies project-scoped:
luarocks install feather --tree ./lua_modulesThen add the local tree to your path:
package.path = package.path .. ";./lua_modules/share/lua/5.1/?.lua"
local Feather = require("feather")Feather uses the global FEATHER_PATH variable to resolve its internal require calls (libs, plugins, etc.). When you load the library via require "feather" or require "lib.feather", FEATHER_PATH is set automatically from the module path.
You only need to set it manually if Feather's files live somewhere Lua's module system can't discover automatically — for example, if the feather/ folder is outside your game's package.path, or if you're loading the library with a custom loader.
-- Set BEFORE requiring feather:
FEATHER_PATH = "lib.feather"
local Feather = require "lib.feather"If you use require("feather.auto"), FEATHER_PATH is set for you — no manual step needed.
The fastest way to get started — one line, all built-in plugins, sensible defaults:
require("feather.auto")
function love.update(dt)
DEBUGGER:update(dt)
-- ...your game code...
endThat's it. DEBUGGER is a global variable created automatically with all plugins registered.
Customize auto setup:
require("feather.auto").setup({
sessionName = "My RPG",
host = "192.168.1.50", -- for mobile debugging
exclude = { "network-inspector" }, -- skip specific plugins
include = { "console" }, -- opt-in plugins (console requires explicit inclusion)
pluginOptions = { -- override default plugin options
bookmark = { hotkey = "f5", categories = { "bug", "note", "todo" } },
},
})Note: The Console plugin is opt-in for safety (it allows remote code execution). Pass
include = { "console" }to enable it.
local FeatherDebugger = require "feather"
local FeatherPluginManager = require "feather.plugin_manager"
local ScreenshotPlugin = require "feather.plugins.screenshots"
local debugger = FeatherDebugger({
debug = Config.__IS_DEBUG, -- Make sure to only run in debug mode
wrapPrint = true,
defaultObservers = true,
autoRegisterErrorHandler = true,
plugins = {
FeatherPluginManager.createPlugin(ScreenshotPlugin, "screenshots", {
screenshotDirectory = "screenshots", -- output folder for captures
fps = 30, -- frames per second for GIFs
gifDuration = 5, -- default duration of GIFs in seconds
}),
},
})
function love.update(dt)
debugger:update(dt) -- Required: drives WS I/O and plugin updates
endFeather uses a push-based WebSocket architecture. The desktop app runs a WebSocket server (port 4004 by default), and your game connects to it as a client.
- Start the Feather desktop app from the releases page
- Run your game with Feather enabled — you'll see:
[Feather] WS client created — connecting to 127.0.0.1:4004
[Feather] Connected to 127.0.0.1:4004
The game automatically reconnects if the desktop app is restarted.
Feather's live WebSocket connection works on mobile too — you just need the device to reach your computer's port 4004.
Similar to React Native's adb reverse, you can forward the device's localhost:4004 to your computer:
adb reverse tcp:4004 tcp:4004Then use the default config — the game connects to 127.0.0.1:4004, which ADB routes to your computer:
local debugger = FeatherDebugger({
debug = true,
-- host defaults to "127.0.0.1", port defaults to 4004
})If the device is on the same Wi-Fi network, point host to your computer's local IP:
local debugger = FeatherDebugger({
debug = true,
host = "192.168.1.42", -- Your computer's local IP
})Tip: Open the Feather desktop app → Settings → scroll to Mobile Connection. Your local IP is auto-detected with a copyable
ws://connection string and ready-to-paste Lua snippet.
If live connection isn't practical (no USB, no shared network), use mode = "disk" to skip WebSocket entirely and only write log files:
local debugger = FeatherDebugger({
debug = true,
mode = "disk", -- No WebSocket, just log files
})Then transfer the .featherlog file from the device and open it in the Feather app using Open Log File.
Feather:init(config) accepts the following options:
| Option | Type | Default | Description |
|---|---|---|---|
debug |
boolean |
false |
Enable or disable Feather entirely. |
host |
string |
"127.0.0.1" |
Desktop IP or hostname the game connects to. |
port |
number |
4004 |
Feather desktop WS server port. |
mode |
string |
"socket" |
"socket" for live WS connection, "disk" for log-file-only mode (no network). |
baseDir |
string |
"" |
Base directory path for file references and deeplinking to VS Code. |
wrapPrint |
boolean |
false |
Wrap print() calls to send to Feather's log viewer. |
maxTempLogs |
number |
200 |
Max number of temporary logs stored before rotation. |
sampleRate |
number |
1 |
Seconds between push cycles (performance, observers, plugins). |
updateInterval |
number |
0.1 |
Interval between sending updates to clients. |
defaultObservers |
boolean |
false |
Register built-in variable watchers. |
errorWait |
number |
3 |
Seconds to wait for error delivery before showing LÖVE's handler. |
autoRegisterErrorHandler |
boolean |
false |
Replace LÖVE's errorhandler to capture errors. |
errorHandler |
function |
love.errorhandler |
Custom error handler to use. |
plugins |
table |
{} |
List of plugin modules to load. |
captureScreenshot |
boolean |
false |
Capture screenshots on error. WARNING: This impacts performance. Use with caution. |
sessionName |
string |
"" |
Custom display name shown in desktop session tabs (e.g. "My RPG"). |
deviceId |
string |
auto-generated | Persistent device ID. Auto-generated and saved to disk if not set. |
writeToDisk |
boolean |
true |
Whether to write logs to .featherlog files. |
retryInterval |
number |
5 |
Seconds between WebSocket reconnection attempts. |
connectTimeout |
number |
2 |
Seconds to wait for initial WS connection. |
apiKey |
string |
"" |
API key for authenticated connections. |
- Only enable
debug = truein development builds — disable it for release for performance and security. - Use
wrapPrint = trueto capture allprint()logs automatically.printfunction will be wrapped with custom logic to send logs to Feather. - Add custom variable observers to monitor your game's state.
Observers are a way to inspect variables values in real-time. They are useful for debugging and observing game state.
debugger:observe("Awesome player instance", player)Feather will automatically capture and log print() calls in real-time, using the output type. You can also manually log using the log and print functions.
debugger:print("Something happened") debugger:log({
type = "awesome_log_type",
str = "Something happened",
})Feather automatically includes trace in errors and logs. You can also manually log traces using the trace function.
debugger:trace("Something happened")Feather will automatically capture and log errors in real-time. You can also manually log errors using the error function.
debugger:error("Something went wrong")The Console is an optional plugin that lets you execute Lua code directly in the running game from the Feather desktop app. It is not included by default — you must explicitly register it and enable eval:
local ConsolePlugin = require("lib.feather.plugins.console")
local debugger = FeatherDebugger({
debug = true,
apiKey = "your-secret-key", -- required for console to work
plugins = {
FeatherPluginManager.createPlugin(ConsolePlugin, "console", {
evalEnabled = true, -- must be explicitly enabled
}),
},
})If the plugin is not registered, cmd:eval commands from the desktop are rejected with a clear error message. No eval code is loaded into your game unless you opt in.
Once enabled, type any Lua expression in the Console tab and see the result immediately — great for tweaking values, inspecting state, or testing functions without restarting.
- Enter to execute, Shift+Enter for multiline input
- Arrow Up/Down to recall previous commands
print()output during execution is captured and displayed inline- Return values are serialized with
inspect()for readable table output - Code runs in a sandbox with an instruction limit to prevent infinite loops from freezing the game
Examples:
return player.health -- inspect a value
player.speed = 500 -- tweak a variable live
return love.graphics.getStats() -- check draw calls, texture memory
print("hello"); return 1 + 1 -- print + return both capturedPlugin options:
| Option | Type | Default | Description |
|---|---|---|---|
evalEnabled |
boolean |
false |
Must be true to allow code execution. |
maxCodeSize |
number |
20000 |
Max characters per eval payload. |
instructionLimit |
number |
100000 |
Lua instruction count before auto-abort. |
maxOutputSize |
number |
100000 |
Max characters in serialized return values. |
Security:
apiKeymust be configured and non-empty in both the game and Feather desktop settings. The Console will refuse to execute code without a matching key.
Warning: DO NOT ENABLE THIS IN PRODUCTION. This is intended for local development only.
Feather comes with a plugin system that allows you to extend its functionality with custom data inspectors. Plugins use a server-driven UI approach: the Lua plugin declares its actions (buttons, inputs, checkboxes) in getConfig(), and the desktop app renders them automatically — no TypeScript code needed. Plugins can be enabled/disabled at runtime from the desktop.
Check out the Feather Plugins documentation for more information.
| Plugin | Description |
|---|---|
| Screenshots | Capture screenshots and record GIFs. Gallery view with download. |
| Console / REPL | Remote Lua code execution (opt-in, requires apiKey). |
| Profiler | Function-level CPU profiling with start/stop. |
| Input Replay | Record and replay input sequences (keyboard, mouse, touch). |
| Entity Inspector | ECS entity browser — register sources, browse and inspect live. |
| Config Tweaker | Live game config editing from the desktop. |
| Bookmark | Mark and navigate to points of interest in game state. |
| Network Inspector | HTTP/WS traffic monitor — wraps socket.http to intercept requests. |
| Memory Snapshot | Heap snapshots, table size tracking, diff between snapshots for leak detection. |
| Physics Debug | Auto-render love.physics World overlay (bodies, joints, contacts, AABBs). |
| Particle Editor | Live ParticleSystem editor — tweak 30+ properties in real-time, export to Lua code. |
| Audio Debug | Inspect love.audio state — track sources, volumes, listener position, source limits. |
| Coroutine Monitor | Track active coroutines — status (running/suspended/dead), yields per frame, lifetime stats. |
| Collision Debug | Visualize bump.lua AABB worlds — bounding boxes, cell grid, collision logging. |
| Animation Inspector | Inspect anim8 sprite animations — current frame, speed, status, flip state. |
| Timer Inspector | Inspect HUMP timer and flux — progress bars, remaining time, cancel from desktop. |
| HUMP Signal | Integration with HUMP signal. |
| Lua State Machine | Integration with lua-state-machine. |
Add plugins to an existing Feather installation without re-running the full installer.
# Install one plugin
bash install-plugin.sh screenshots
# Install several at once
bash install-plugin.sh screenshots profiler console
# Install from a specific branch into a custom directory
FEATHER_DIR=lib/feather FEATHER_BRANCH=dev bash install-plugin.sh entity-inspector config-tweaker
# Pipe directly from GitHub
curl -sSf https://raw.githubusercontent.com/Kyonru/feather/main/scripts/install-plugin.sh | bash -s -- screenshots profilerRun without arguments to see the full list of available plugins:
bash install-plugin.shAfter installing, register the plugins in your setup:
require("feather.auto").setup({
include = { "screenshots", "profiler" },
})The apiKey option is used to protect your game from unauthorized access. Set it in the game config and match it in the Feather desktop app:
local debugger = FeatherDebugger({
apiKey = "your-api-key",
})Feather is not meant to be used in production / final builds. It is meant to be used during development and debugging. To improve performance, you can set the debug option to false in the game.
- OverlayStats by Oval-Tutu is a great way to visualize your game's performance in real-time on the game, the performance plugin is inspired by it.
See CHANGELOG.md for the full release history.
Latest — v0.6.0 — The one with the plugin ecosystem
- 18 built-in plugins: screenshots, console, profiler, input replay, entity inspector, config tweaker, bookmark, network inspector, memory snapshot, physics debug, particle editor, audio debug, coroutine monitor, collision debug, animation inspector, timer inspector, HUMP signal, lua-state-machine
- Grouped card layout for plugins with many inputs (server-driven UI)
- Plugin enable/disable toggle from desktop
- Zero-config
auto.luaentry point withexclude/include/pluginOptions - curl-pipe-sh installer script (
install-feather.sh) - Mobile connection discovery — auto-detected local IP in Settings
- Per-session version mismatch warning on session tabs
- Disk mode for log-only debugging on Android/iOS
- Console / REPL with sandbox and instruction limit
- WebSocket push-based architecture with multi-session support
- Plugin error isolation and auto-disable after repeated failures
DISCLAIMER: The license only meant to apply to products that directly replicate the logic/purpose of this tool. It doesn't not apply to games created using this tool. Feel free to disregard if the tool is used only as a devtool to ship games.
Full license: See LICENSE.md
- LoveBird by rxi — the original LÖVE debugger that inspired Feather's core concept
- Love-Dialogue by Miisan-png — plugin system architecture reference
- Flipper by Facebook — UI/tooling patterns for desktop debugging apps
- HUMP by vrld — Class system, Timer, Signal
- anim8 by kikito — sprite animation library
- flux by rxi — tweening library
- bump.lua by kikito — collision detection library
- lua-state-machine by Kyle Conroy — state machine for Lua


