Companion app for Bluetooth enabled dice.
WARNING: there will be constant breaking changes, and bad builds, for the foreseeable future. Please report issues or requests.
- Features
- Platforms
- Install
- Home Assistant
- API Server
- Webhooks & Discord
- dddice Integration
- Rule Scripting
- Connect multiple supported Bluetooth dice
- Pixel Dice
- GoDice
- Virtual Dice (with removal support)
- Track roll history
- Custom rule scripting via DSL
- Roll types & aggregates
- sum, max (advantage), min (disadvantage)
- Blink the lowest or highest die when rolling max or min
- Visual feedback on roll
- Custom blink colors per die
- Rolling flash animations (Pixels only) — configure color and animation style (Strobe, Pulse, Breathe)
- Preview animations directly from settings
- Pixels LED animation profiles — browse and flash built-in profiles (hash-identical to the official app), see which profile is on the die, and create/edit/import/preview your own. Works across all Pixels die types (d4–d20).
- Home Assistant integration for smart light control
- Webhook and Discord rule actions — fire HTTP requests or Discord embeds on roll result
- dddice integration — mirror physical dice rolls to a virtual tabletop room
- API server to get the latest roll (local network only)
GET http://<device-ip>:8080/api/last-roll- Not available on web
- Authoring guide and reference: docs/dsl/roll_feathers_dsl_v1_1_guide.md
- Rule examples (including high/low variants): docs/dsl/
- CLI-style tester usage: docs/dsl/dsl_test_harness_usage.md
- Quick start: run
flutter test test/tools/dsl_tester_cli_test.dart --with env varsRULE_TEXTandDICE(and optionalMODIFIER,THRESHOLD). See the usage doc for details and examples.
- Android
- iOS
- build from source
- macOS
- Windows
- Linux
- build from source
- Web
- Chromium-based browsers
All installers are currently unsigned, meaning you'll have to click past various security warnings to install the app.
- download apk or appbundle
- open downloaded apk
- a security popup will say that apps from unknown sources can't be installed
- click on the settings option
- toggle "allow from this source"
- click install
- The phone may scan the app for malicious code, allow that, continue installation afterward.
- download & open
dmgfile - drag the roll feathers app into the application folder
- close and eject the dmg
- open the roll feathers app from the applications folder, there will be a security warning
- go to Settings → Privacy & Security → Security
- there should be a button to open roll feathers anyway
- Open anyway
- allow bluetooth permission when prompted
Installed and portable versions are available
- download exe file
- open and run, follow installer prompts
- download zip
- extract to folder
- there is not a containing folder in the zip, extract in a new empty folder
- run the contained
exe
A Home Assistant integration is available for blinking lights on rolls.
- Create an access token
- Enable Home Assistant in the app
- Paste the access token plus the HA url and a default light entity to control into the Home Assistant Settings Page
- The target entity can be set per die in the connected dice settings
For Home Assistant to allow external access from the web client, the HA cors settings need to be configured. See the Home Assistant documentation for details.
Example Config Block
http:
cors_allowed_origins:
- https://rollfeathers.ungawatkt.comThe api server binds to all available interfaces on port 8080.
address: http://localhost:8080
GET /api/last-roll: return the most recent roll
DSL rule actions can fire outbound HTTP requests or Discord embeds when a result range matches.
- Enable Webhooks in app settings to allow outbound requests (gated globally)
- Add
action webhook [GET|POST] <url>oraction discord <webhook_url>to anyon resultline in a rule - POST sends a structured JSON payload (
RollResultDTO) with rule name, timestamp, aggregate value, matched range, and dice details - GET sends
ruleandaggregateas query parameters only - Errors are caught and logged; dice behavior is never interrupted
See Rule Scripting for the DSL syntax and the built-in Webhook Example rule as a starting point.
Rolls made with connected physical dice can be mirrored to a dddice virtual tabletop room in real time.
- Enable dddice in app settings
- Enter an API key (create one at dddice.com) — or leave blank to join as a guest under the name
bees - Select a room from the room picker (rooms are fetched from your account)
- Optionally set a theme per die in each die's settings
- Each physical dice roll is sent to the configured dddice room using the die's assigned theme
- Guest sessions automatically create a temporary room on first roll
- Themes are stored per die identifier and persist across sessions
Rule scripts define custom actions triggered by a dice roll via a built-in DSL. Scripts filter dice into named selections, aggregate over them, and execute actions when the result falls in a specified range. See the DSL v1.1 Authoring Guide for the full reference and the default rules for working examples.
define <name> ["Display Name"] for roll <dice matcher>
[make selection @<NAME> [from (@<PARENT> | $ALL_DICE)]
[with <transform>]
...]
use selection (@<NAME> | $ALL_DICE)
aggregate over selection <aggregate>
on result <range> action <action>
...
Blocks are ordered: all make selection blocks that a use selection needs must appear before it.
$MODIFIER: modifier value (defaults to 0)$THRESHOLD: threshold value (defaults to 0)$ALL_DICE: all dice in the roll$ROLLED_COUNT/$ROLLED: number of dice rolled (substituted at evaluation time)$MAX/$MIN: highest and lowest face values (substituted at evaluation time)
Matches which roll triggers the script, using standard dice notation with wildcards:
*d*: any number of any die type1d10,1d00: one d10 and one d00 (percentile — enter0as face count to create a virtual d00)2d20: exactly two d20s*d6: any number of d6s3d*: exactly three dice of any type
Build a named selection by filtering and transforming the source dice:
with top N/with bottom N: keep the N highest / lowest dicewith match [a:b]: keep dice whose value falls in the range [a:b]with dupes [a:b]: keep dice in value-buckets with multiplicity in [a:b] (e.g.[2:*]for any duplicates)with over N/with under N: keep dice strictly above / below Nwith offset X/with mul X/with div X: apply math to all values in the selection
Transforms apply in order. Multiple with lines can be chained within one make selection block.
Applied inside a use selection block to produce the numeric result:
sum: total of all valuesmin/max: lowest / highest valueavg: averagecount: number of dice in the selection
[ / ] are inclusive endpoints; ( / ) are exclusive; * is open (unbounded).
[*:*]: any value[10:*]: 10 or higher[*:10): 9 or lower(2:5): 3 or 4[$MIN:$MIN]: exactly the global minimum (all dice tied)
action blink [color]: blink the selection's dice the given color (default white)action blink $ALL_DICE [color]: blink all rolled diceaction sequence [loops] [colors...]: cycle the selection's dice through a color listaction webhook [GET|POST] <url>: fire an HTTP request (requires Webhooks enabled in settings)action discord <webhook_url>: post a Discord embed
Percentile roll (1d10 + 1d00), blink a color per bracket, rainbow on 99+:
define percentiles "Percentiles (1d10,1d100)" for roll 1d10,1d00
use selection $ALL_DICE
aggregate over selection sum
on result [0:10) action blink red
on result [10:25) action blink orange
on result [25:50) action blink yellow
on result [50:75) action blink green
on result [75:90) action blink blue
on result [90:99) action blink purple
on result [99:*) action sequence 1 red orange green blue violet
Blink the top die after applying a modifier (advantage with modifier):
define maxWithModifier "Max (with Modifier)" for roll *d*
# blink one die with the highest value after applying the modifier
make selection @ALL_MOD from $ALL_DICE
with offset $MODIFIER
with top 1
use selection @ALL_MOD
aggregate over selection max
on result [*:*] action blink blue
Highlight high (green) and low (red) dice; blink purple if all dice tie:
define highLowAllTiesExclusive "High/Low/Tie All" for roll *d*
make selection @ALL_MAX with match [$MAX:$MAX]
make selection @ALL_MIN with match [$MIN:$MIN]
make selection @DUPE_ANY with dupes [2:*]
# All-equal → purple only
use selection @DUPE_ANY
aggregate over selection count
on result [$ROLLED:$ROLLED] action blink purple
# Non-all-equal → highs and lows
use selection @ALL_MAX
aggregate over selection count
on result [1:$ROLLED) action blink green
use selection @ALL_MIN
aggregate over selection count
on result [1:$ROLLED) action blink red