Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×

[Script] Gaslight — Per-Player Token Illusions

1782859023

Edited 1788608320
Aerotech
Pro
API Scripter
Gaslight — Per-Player Token Illusions Different players. Same token. Different reality. The Problem You've built the perfect encounter. The monster manifests as each player's greatest fear — the fighter sees a lich, the wizard sees their dead mentor, the rogue sees a locked door they can't pick. You set it all up. You tested it with alt accounts. It seemed to work. Session night. It does not work. Not even a little bit. Turns out, making one token look different to each player on Roll20 is... not easy. Your options are: run a separate map for each player (nightmare), use theater of the mind (defeats the point of a VTT), or give up on the idea entirely. Most GMs give up. Or maybe you want actual stealth. Not " Markos' passive perception is 22, so now everyone sees the assassin sneaking up on them " stealth. Real stealth — where Markos' player, Zack, can see the hidden assassin (because their passive perception beats it) and Rhea's player, Wyatt, literally cannot. The token isn't there for them. They don't know it exists. And when Zack says "What the heck is that?!" and Wyatt responds with "What the heck is what?! ", then you know you've got a wonderful game ahead of you. Man, if only something like that were possible... The Solution Gaslight creates per-player illusions on tokens. Each player can see a different image, name, bar values, aura — anything — on the same token, while the GM sees the truth. One creature, multiple realities. Under the hood, Gaslight gives each player their own page with mirrored tokens. Changes to the real token propagate instantly. Players don't know they're on separate pages — it looks and feels like they're on the same map. Use Cases Truesight — Everyone in the party loves Robert the rogue, but one day, Sally the sorcerer casts true sight and finds out that Robert is a changeling. Only Sally knows this information. Does she keep it a secret? Real stealth — Tokens are only visible to players whose passive perception beats the DC. Everyone else doesn't see them at all. No "okay just pretend you didn't see that token." Asymmetric information — Everyone in the party knows the friendly merchant as Hassalfax. It says it right there on his nameplate, see? One of the players knows him as Morganth. He'll never forget that face. Illusions — The party sees an empty corridor. The rogue checks the hallway and finds no traps, but a hidden loot stash is revealed to her when she gets close enough. She decides to lead the party down the opposite end of the hallway so she can come back later and keep all of the loot for herself. How It Works Manual Mode (for preparing the gaslight in advance) Quick Mode (for on-the-fly gaslighting — Currently for Experimental Sandbox only) 0. One-Time Setup (you only need to do this once ever): Create a blank page, name it  GL-SCRATCH  and duplicate it so there is one "scratch" page per player (you can create more "scratch" pages if you want to allow for "nested" gaslighting) 1. Duplicate your map page — one copy per player. If you have five players, there should be six pages (one original and five copies). 1. Run  !gaslight quick  with player's tokens selected on your map page  — Gaslight will turn your "scratch" pages into copies of the map page, assign players to their own copies, link up tokens, and sync everything. This essentially does steps 1, 3, and 4 of the Manual Mode automatically. 2. Customize each player's copy (different token art, hidden/visible tokens, different names, etc.). 2.  Customize each player's copy (different token art, hidden/visible tokens, different names, etc.).  Note that unlike Manual Mode, players are already on their pages and can potentially see what changes you make to their respective pages. 3. Run !gaslight setup — Gaslight detects the pages, assigns players, and creates all config automatically. Gaslight will tell you in the chat what this gaslight group is named. The name will also show up in the top left corner of the map as text on the GM layer in addition to other information. DO NOT EDIT this text. 4. When you are ready to start the gaslight during the campaign session,  run !gaslight split <group-name> — Gaslight detects the pages, assigns players, and creates all config automatically From that point on, the GM works on the master page. Move a token on master — it moves on all player pages. Players move their own tokens — it syncs back. But each player only sees what's on THEIR page. If you run a command on a token on the master page, it will run that same command on each player's page. If you run a command on a token on a player's page, it will only run on their page. When you're done: !gaslight merge  brings everyone back together to wherever the "banner" page is. If "scratch" pages were used (Quick Mode) then those pages will be automatically cleared and returned to blank pages named   GL-SCRATCH Commands !gaslight examples  — Shows examples to help the user get started !gaslight setup [optional-group-name] [players...] — Quick-configure a group from pre-duplicated pages. If no group name is provided, one will be generated for you. If players are provided, they will be used for the split rather than being automatically determined. !gaslight test <group-name> — Dry-run: check linking, show warnings before activating !gaslight split <group-name> — Activate (assign players to pages, link + sync tokens) as well as turns on the HUD on the master page. !gaslight merge [<optional-group-name>] — Deactivate a split, optionally by name. If no name is provided, the latest split will be used. Players assigned to pages in the split will be moved to their prior split (or to the banner page if there is no prior split). Scratch pages used in a split will be cleared and restored to blank pages for reuse in another split. !gaslight merge-all — Deactivates all splits, returning all players to the banner page and restoring all scratch pages. !gaslight link [<name>|new] — Manually link tokens across pages !gaslight relay <players> <!command> — Run a command on specific player pages !gaslight view [master|<player>|off] — Preview what commands relay automatically (master = relay commands to all players, off = relay to no players) !gaslight stage [--default on|off] — New tokens on the master page will not sync to player pages until you run this command. Allows you to tweak what you want before everyone sees it.   --default handles it on the character sheet so it will be staged by default when placed on a player page. !gaslight status — Show active groups and links !gaslight sync  [--default]  [<props>|all|reset] — Per-token sync control.  --default  handles it on the character sheet so it will be synced by default. !gaslight desync  [--default] [<props>|all] — Exclude props from sync . --default handles it on the character sheet so it will not be synced by default. !gaslight init [sync|trim] — Sync initiative with HUD !gaslight hud [on|off|reset] [init|view|reticle] — Toggle HUD elements !gaslight var [--set|--get|--del] <var_name> [< --set_ value>]  — Manage gl_* variables (view-aware) !gaslight --help — Full reference HUD and Initiative When players are split onto pages, a HUD will display on the master page. This HUD will show the active view as well as an interactive initiative tracker. You might find this easier to use than the normal initiative tracker due to how initiative changes work with gaslight running. Specifically, gaslight will make one turn per-token per-page. So when someone rolls initiative for their character, in a party of 3, that would mean 4 turns for that one character will be in the initiative tracker (one for each player and one for the master page). You CAN use the built-in turn tracker and it does automate past the extra turns for you, but the gaslight HUD is designed to make things look clean again. Automation Gaslight allows for basic scripts to be written in handouts and assigned to maps with pins. Combined with RollCapture (for extracting roll results) and the relay system, you can build things like: Automatic stealth — tokens appear/disappear per player based on passive perception vs. Stealth DC Triggered reveals — a player passes an Insight check and the NPC's token image changes only for them Per-player FX — different spell effects visible to different players The building blocks are there. What you do with them is up to you. Dependencies Gaslight orchestrates several scripts behind the scenes: Anchor — Spatial sync (position, rotation, scale) between linked tokens Mirror — Property sync (HP, status, light, etc.) between linked tokens ScriptKit  — Help system, versioning, migration, and examples. RollCapture — Read results of rolls for triggering scripted outcomes Meta-Toolbox — Allows scripting of gaslight automation through scripts like Fetch, SelectManager, and APILogic All install from the One-click installer. Install Available in the One-click installer. Requires: Anchor, Mirror, ScriptKit, RollCapture, ZeroFrame, SelectManager, Fetch, APILogic GitHub Source | Wiki Page
This looks like some good work!
1782865799
Gauss
Forum Champion
Hi Aerotech,  How are you handling the Turn Order without it becoming a jumbled mess for the GM? 
if possible, I'd love to see this in action (YT vid)
1782869331
Aerotech
Pro
API Scripter
Good point, Gauss. Actually, I hadn't considered that. I will figure out a way for that to not suck and add it in an update soon.
1782869891
Aerotech
Pro
API Scripter
Novercalis, I plan to make a YT video soon.
1783039499
timmaugh
Pro
API Scripter
Interesting (and ambitious) idea! I should point out that if people are going to install several of the metascripts, they should at probably include ZeroFrame (or just get the full Toolbox). This is because ZeroFrame gives an established, expected order (which can be modified in the command line) to the order the other scripts run. For instance, if you had Fetch and SelectManager installed, but not ZeroFrame, then a formation like this: {& select @(Thungus.gmnotes) } ...will work if Fetch is installed *before* SelectManager, but not if it's installed after. The Fetch construction will resolve, probably returning a list of token names/ids to select, then SelectManager will select those referenced tokens. If SelectManager tries to go first, it will look for a token named "@(Thungus.gmnotes)" and fail to find it. Meanwhile a formation like: @(selected.gmnotes) {&select Thungus} ...will work if Thungus is selected first, then Fetch can return data from the selected token. That means that SelectManager needs to be installed before Fetch. With ZeroFrame installed, SelectManager will default to going before Fetch unless you change it. That lets you have predictability between different games (the same command will work for all your games), and it means that you can change the order to make any construction function correctly. For instance, the first example could be made to work with backslashes to defer constructions: {\& select @(Thungus.gmnotes) } ...or by adding an ordering tag added to the line: {& select @(Thungus.gmnotes) } {&0 fetch sm}
1783116544

Edited 1783280876
Aerotech
Pro
API Scripter
v2.1.0 Update — Initiative Tracking + HUD System @Gauss — great question, and it's been solved! Here's how: Initiative Tracking Gaslight now fully manages the turn order across gaslit pages: When a token is added to initiative, all linked copies are added automatically at the same value When you click Next/Prev, linked children are auto-skipped — you only ever land on master tokens or custom turns Sorting, dragging, and removing entries all propagate to linked copies Custom turn round calculations (e.g. +1 for a round counter) work correctly even through our API-driven rotation The GM sees the full turn order (with linked copies for each player page), but never has to interact with the copies — they're skipped automatically. Players on their own pages see only their tokens in the tracker, so it looks normal to them. On-Canvas HUD To make it even easier, there's now an optional initiative HUD on the master page foreground layer: A frame showing the deduped turn order (one entry per creature, not per-page copy) Current turn highlighted with a diamond indicator Drag tokens vertically to reorder initiative Swipe tokens horizontally past the frame edge to jump to that turn Custom turns shown as pins with their names Fully resizable and customizable (frame, text, colors, positioning all adjustable by the GM) A view indicator showing the current relay state (ALL / OFF / specific player) Toggle with !gaslight hud or !gaslight hud reset to try it out. gl_ Variable Management New command for managing script variables directly: !gaslight var --set stealth_result 15 !gaslight var --get stealth_result !gaslight var --set stealth_result 20 --setch stealth_dc 12 !gaslight var --del stealth_result !gaslight var --silent --set stealth_result 25 View-aware: reads and writes respect the current view target. When view is set to a specific player, var only affects that player's copy of the token. Other v2.1.0 additions: !gaslight sync / !gaslight desync — per-token control over what stays in sync !gaslight view off — disable command relay for GM-only changes Formula support for custom turns (round counters, etc.) !gaslight var — manage gl_* variables with view-aware propagation and automatic script triggering Script eval fixes — per-player automation (like stealth visibility) now works correctly without affecting other players Stage improvements — follows current view, full property sync via Mirror
1783116980

Edited 1783164936
Aerotech
Pro
API Scripter
timmaugh said: Interesting (and ambitious) idea! I should point out that if people are going to install several of the metascripts, they should at probably include ZeroFrame (or just get the full Toolbox). This is because ZeroFrame gives an established, expected order (which can be modified in the command line) to the order the other scripts run. For instance, if you had Fetch and SelectManager installed, but not ZeroFrame, then a formation like this: I have added ZeroFrame, APILogic, and Fetch as dependencies. They aren't technically required, but they are needed for the more important parts of the script and the consistency is important.
1785445824

Edited 1785445969
Aerotech
Pro
API Scripter
v2.2.0 Update — Initiative HUD Overhaul + Interactive Guides Big update! The initiative HUD has been completely rebuilt, and Gaslight now includes interactive walkthroughs to help new users get started. Initiative HUD — Pin-Based Redesign The initiative tracker has been rebuilt from the ground up using Roll20 pins: Each combatant is a pin showing their token image and initiative value Swipe right = next turn, Swipe left = previous turn Drag up/down = reorder in initiative (visual gap-based insertion) Delete a pin = remove from initiative Resize any pin = all pins scale together Current turn marked with a diamond highlight (drag to reposition in frame) Reticle on the map highlights the active combatant's token (inherits diamond styling, independently customizable) Frame resize controls how many slots are visible Proportional slot distribution keeps the diamond centered New command: !gaslight init [sync|trim] — run this after plugins (like GroupInitiative) add tokens to initiative, since Gaslight can't detect Campaign().set('turnorder') changes automatically. Interactive Guides Gaslight now includes step-by-step guided walkthroughs powered by ScriptKit. Run !gaslight examples to browse: getting-started — Setup, split, merge from scratch core-mechanics — Staging, linking/unlinking, sync/desync, token deletion initiative-hud — All gestures, customization, reticle, !gaslight init relay — Auto-relay, view switching, manual relay scripting — Build a "winds of magic" GLS script step by step Each guide validates your progress, auto-advances on commands, and uses annotations/pings to point at relevant elements. Token Management Improvements !gaslight stage --default on|off — set gaslight_stage on the character so tokens auto-stage on placement !gaslight sync --default / desync --default — set character-level sync config for new tokens Asymmetric unlink/delete — deleting/unlinking a parent (master or controlling player's copy) cascades to all copies; deleting a non-parent copy just detaches it Stage + initiative — staging a token mid-combat automatically adds linked copies to the turn order Scripting Fixes gl_* variables now properly fall back from token gmnotes to character attribute (fixes --setch not being read by scripts) Fixed @(target.gl_field[0]) regex — the [default] suffix was preventing compProp registration All example scripts fixed: APILogic uses = not == for equality Dependencies Now requires ScriptKit (for help system and examples). ScriptKit was recently merged to the one-click library.
1788005398

Edited 1788005477
Aerotech
Pro
API Scripter
v2.2.1 + v2.2.2 Update — Bug Fixes & Polish A maintenance pass focused on stability. Most of the work landed in v2.2.1 (a big batch of fixes across linking, sync, and the initiative HUD), with v2.2.2 being a small housekeeping bump. Linking & Sync Fixes (v2.2.1) !gaslight link now re-establishes links for tokens that are already in active groups !gaslight sync removes !excludes correctly and propagates to all linked copies !gaslight desync now applies immediately — the parent rebuilds its links and the child hands off to Anchor.untrackComponents !gaslight stage --default on checks for marketplace images Fetch/ compProp resolution now returns the token gmnotes value before falling back to the character attribute Initiative HUD Fixes (v2.2.1) HUD hides when the turn order is empty and reappears when turns are added HUD text offset no longer corrupts after a turn advance Turn reticle updates correctly after a pin is deleted Custom turns are properly deduplicated using immutable key matching pr=0 no longer displays as empty text Batch additions are no longer lost to a turn-order race condition (processing is now debounced) Quality of Life (v2.2.1) Warnings and errors now show clickable token images that ping the token's location (requires ScriptKit 1.2.0) !gaslight test output condensed to a summary plus warnings only Tutorial now uses guide pings on HUD elements and customization steps v2.2.2 — Housekeeping Added version dates to the changelog so ScriptKit can answer date-based "what's new" queries Added a startup log line confirming the loaded version in the API console Dependencies The clickable-image warnings/errors require ScriptKit 1.2.0 or later.
1788605819
Aerotech
Pro
API Scripter
v2.3.0 Update — One-Command Setup, Nested Splits & Marketplace-Safe Cloning The headline feature: !gaslight quick sets up a whole per-player split in a single command — no more manually duplicating pages, running setup , then split . !gaslight quick [group] [players...] Point Gaslight at your master page and it clones everything onto player pages, configures the group, and splits — all at once: Uses any existing duplicates of the master page for player pages, and fills any shortfall from reusable GL-SCRATCH pages (keep a few blank ones around named GL-SCRATCH ; Gaslight clones your master onto them and recycles them on merge). Clones the full page: tokens (including Marketplace art, via graphic.createCopy ), walls, doors, windows, pins, paths, and text — preserving z-order. Gaslight's own config text and [GLS] script pins are excluded from the clones. Validates all target pages up front and aborts cleanly (changing nothing) if a page hasn't been initialized — no half-finished setups. Requires the experimental sandbox for now, since it depends on Roll20's new graphic.createCopy . On the legacy sandbox, quick politely tells you to switch (or fall back to manual setup ). Marketplace-Safe Staging !gaslight stage (and all cloning) now uses createCopy when available, so tokens with Marketplace images keep their art and sides when copied to player pages — previously this failed because the API rejects Marketplace URLs. Auto-stage failures are now reported to the GM instead of failing silently. Nested Splits You can now run a second split without tearing down the first. Gaslight tracks a per-player page stack, so: !gaslight merge (no argument) ends the most recent split and returns each player to the previous split they're still part of (or the banner page if none). !gaslight merge <group> ends a specific split anywhere in the stack. !gaslight merge-all ends everything at once. ⚠️ Behavior change: !gaslight merge with no argument used to end all active splits. That's now !gaslight merge-all . Bare merge ends just the top of the stack. Optional Group Names setup and quick no longer require you to name the group — a readable name like arcane-dragon is generated automatically (and shown in the reply). Provide your own as the first argument if you'd like. The first argument is treated as a player name unless it doesn't match a player. Adaptive Getting-Started Guide The getting-started walkthrough ( !gaslight examples ) now adapts to your sandbox — it teaches the one-command quick flow when available, or the manual duplicate/ setup / split flow otherwise, with a separate manual-setup guide for learning the manual approach. Dependencies Requires ScriptKit 1.4.0 or later (for the readable name generator).
ever managed to get a YT tutorial vid?
1788697888
Aerotech
Pro
API Scripter
I have some errands to run, but I will try to get around to making something small on YT some time this week.