A World of Warcraft Classic addon that makes action buttons visually respond to gameplay situations.
Optimal play requires tracking cooldowns, resources, buffs, debuffs, and enemy casts simultaneously - mental overhead that pulls attention away from positioning and split-second decisions. Prism moves that tracking to your peripheral vision: buttons glow when abilities should be used, turn red when blocked, or show a golden border when already active.
All action buttons benefit from automatic visual feedback (range, resources, cooldowns) without any configuration. Custom rules let you add ability-specific logic on top.
- Download Prism from CurseForge
- Extract to your
World of Warcraft/_classic_/Interface/AddOns/folder - Restart WoW or type
/reloadif already in-game
To configure Prism in-game, type /prism to open the configuration window.
Ready-to-use configurations are available for various classes. For example, here's a complete Rogue configuration.
To use an example configuration:
- Type
/prismin-game to open the configuration window - Click "New Document" to create a new document
- Copy the content from the example file and paste it into the document
- Save the document
Here's a practical example - configuring the Druid's Growl ability with a macro that automatically shifts into bear form:
#showtooltip Growl
/cast [noform:1] Bear Form
/cast Growl
/startattack
spell: "Growl"
blocked: @player{!canShiftInto(bear)} | @target{dead | !harm}
checked: @target{ownDebuff(infer) | targeting(@player)}
enabled: @target{!casting}
flashing: @player{!canShiftBackFrom(bear)}
glowing: @player{combat & grouped & druidForm(bear)}This makes Growl:
- Red when lacking mana to shift into bear form or target is dead/friendly/missing
- Golden border when Growl debuff is active or target is already targeting the player
- Grayed out when target is casting
- Flashing when lacking mana to shift back from bear form after using Growl
- Glowing when in bear form during combat in a group
The visual state system follows a strict hierarchy:
- Default state - buttons are enabled by default unless a rule explicitly disables them
- Cascading effect - if an ability is blocked, depleted, or checked, it becomes disabled and cannot glow or flash
- Precedence - default rules always win - if the game determines that an ability is blocked, depleted, or checked, custom rules cannot override this
Even without custom rules, Prism applies these defaults to all action buttons based on game mechanics:
- Out of range → Blocked (red, disabled)
- On taxi/flight path → Blocked (red, disabled)
- Out of mana/rage/energy → Depleted (blue, disabled)
- Currently casting/channeling → Checked (golden border, disabled)
- Auto-repeating actions → Checked (e.g., Attack, Auto Shot, Shoot)
- On cooldown → Disabled (grayed out, but only for cooldowns longer than the global cooldown)
Note on cooldowns: Abilities on cooldown are automatically disabled and grayed out, but only when the cooldown is longer than the global cooldown (1.5s). This preserves visual states (glowing, flashing) during the GCD so important cues aren't lost when global cooldown activates.
Combined states: When an ability is both blocked and depleted, the overlays combine to show purple (red + blue), giving immediate visual feedback about multiple issues.
The cascading system prevents conflicting visual states:
- Disabled states (blocked, depleted, checked) prevent flashing and glowing
- Flashing takes priority over glowing when both conditions are met
For example, with Aspect of the Hawk:
spell: "Aspect of the Hawk"
checked: @player{buff(infer)} # Shows golden border when buff is active
glowing: @player{autoShooting} # Glows when auto-shootingOne might think glowing: @player{autoShooting & !buff(infer)} is needed to prevent glowing when the buff is already active. But that's unnecessary! When checked is true (buff is active), the button becomes disabled and cannot glow anyway. The cascading effect handles this automatically, making configurations cleaner and easier to write.
The Prism configuration language is built around a simple pattern: defining rules that evaluate predicates within scopes to control how action buttons appear.
- Predicate - the atomic check:
combat - Condition - combine predicates with logic:
combat & health(<20%) - Scope - evaluate conditions on specific units:
@player{combat} - Rule - combine scopes to control visual states:
@player{combat} & @pet{dead}
Each spell configuration starts with the spell name, followed by optional visual state rules:
spell: "Aspect of the Hawk"
blocked: <rule> # Optional
depleted: <rule> # Optional
checked: <rule> # Optional
enabled: <rule> # Optional
flashing: <rule> # Optional
glowing: <rule> # OptionalEach configuration handles one spell only. Spell names must match the game's language - use "Aspect of the Hawk" for English clients, "Aspekt des Falken" for German, etc.
Items use their item IDs instead of names:
# Healing Potions
item: 118, 858, 929
blocked: <rule> # Optional
depleted: <rule> # Optional
checked: <rule> # Optional
enabled: <rule> # Optional
flashing: <rule> # Optional
glowing: <rule> # OptionalItems use IDs for language compatibility (IDs work across all game languages), to handle multiple variants (many different healing potions can share one configuration), and for easier maintenance.
Use Wowhead Classic - the ID is in the URL:
https://www.wowhead.com/classic/item=118/minor-healing-potion→ ID is118https://www.wowhead.com/classic/item=858/lesser-healing-potion→ ID is858https://www.wowhead.com/classic/item=929/healing-potion→ ID is929
Only the states that need customization need to be specified. All action buttons already benefit from Prism's default behaviors - custom rules add specific conditions on top:
spell: "Aspect of the Beast"
checked: @player{buff(infer)}This simple configuration benefits from all the default addon rules (cooldown visualization, out of range indication, missing resource alerts) while adding just one custom rule to show when the aspect is active.
These operators combine predicates into conditions:
&- AND (both must be true)|- OR (either can be true)!- NOT (inverts the result)()- parentheses for grouping
Units tell the game who to check - the player, the player's current target, the player's pet, etc.
@player- the player character@target- the player's current target@pet- the player's pet (Hunter, Warlock)@mouseover- unit under the mouse cursor@focus- the player's focus target (if supported)@party1through@party4- party members@raid1through@raid40- raid members
Units can have the :help modifier to enable smart targeting:
:help- automatically targets yourself when holding self-cast modifier keys, when target doesn't exist, or when target can't be assisted
# Smart targeting, e.g. for bandages and healing spells
@target:help{health(<50%)}The infer keyword automatically determines the appropriate buff/debuff name:
- Spell configurations: Uses the spell name
- Item configurations: Uses the item's spell effect name
spell: "Serpent Sting"
checked: @target{ownDebuff(infer)} # Checks for "Serpent Sting" debuff
item: 8766 # Morning Glory Dew
checked: @player{buff(infer)} # Checks for "Drink" buffThe always predicate is unconditionally true - useful for "always glow":
glowing: @target{always} # Always glows when there is a hostile target- Percentages:
50%,100% - Durations:
2s,10s - Comparisons:
>,<,>=,<=
When no comparison operator is specified, values check for equality. For "not equal", omit the operator and use the ! negation instead.
@player{health(<30%)} # Health below 30%
@player{health(100%)} # Health at full
@player{!health(100%)} # Health not full (damaged)
@target{comboPoints(>=4)} # Target has 4 or more combo pointsPredicates are the individual checks that evaluate to true or false. They're used within scopes to test specific properties of units.
alive- unit is alivedead- unit is deadexisting- unit existsmissing- unit doesn't exist
@pet{dead | missing}buff(name, count?, duration?)- unit has buff with optional stack count and remaining durationownBuff(name, count?, duration?)- unit has buff cast by the playerdebuff(name, count?, duration?)- unit has debuff with optional stack count and remaining durationownDebuff(name, count?, duration?)- unit has debuff cast by the player
@target:help{buff(infer)}
@target{ownDebuff("Sunder Armor", 5, >5s)}cursed- unit has curse debuffdiseased- unit has disease debuffmagicBuff- unit has magic buffmagicDebuff- unit has magic debuffpoisoned- unit has poison debuff
@pet{!cursed & !diseased & !magicDebuff & !poisoned}energy(amount)- energy levelhealth(percentage)- health percentagemana(percentage)- mana percentagerage(amount)- rage level
@player{mana(<20%) | health(<30%)}attacking(@unit)- unit has highest threat on specified unitcasting- unit is casting any spellcombat- unit is in combatcomboPoints(number)- unit has combo pointstargeting(@unit)- unit is targeting specified unittrivial- unit is too low level to award experience or honor
@target{targeting(@player) & !casting}
@target{!trivial} # Target is worth experience/honorblocked- unit blocked the last attackdodged- unit dodged the last attackparried- unit parried the last attack
@player{dodged}bleedable- unit can be affected by bleed effectscreature(type)- unit is specific creature type (beast,critter,demon,dragonkin,elemental,giant,humanoid,mechanical,totem,undead)elite- unit is elite, rare elite, or world bossharm- unit is hostilehelp- unit is friendlyis(@unit)- unit is the same as specified unitlevel(number)- unit's levelnpc- unit is an NPC (not a player)partyMember- unit is in the player's party
@target{creature(beast)}
@target{elite & level(>=59)}falling(duration?)- player is falling with optional duration checkmounted- player is mountedrunning(duration?)- player is running (7+ yd/s, excluding mounted and taxi) with optional duration checkswimming- player is swimmingsubmerged- player is underwater
@player{falling(>2s)}
@player{running(>3s)}grouped- player is in a groupindoors- player is indoorsoutdoors- player is outdoorsresting- player is resting
@player{outdoors & !grouped}equipped(type)- player has equipment type equipped (dagger,meleeWeapon,shield)form(number)- player is in specific formstance(number)- player is in specific stancestealthed- player is stealthed
@player{stance(1) & equipped("shield")}
@player{equipped("meleeWeapon")} # Checks if a usable melee weapon is equipped in main handtalent(name, rank?)- player has talent with optional rank check
@player{talent("Tactical Mastery", >=2)}autoShooting- player is auto-shootingmeleeAttacking- player is melee attacking
@player{autoShooting}ammo(amount?)- player has ammo with optional amount check
@player{ammo} # Has any ammo
@player{ammo(>200)} # Has more than 200 ammo
@player{ammo(<50)} # Has less than 50 ammo (warning)itemCount(itemId, amount)- item count in player's inventory
@player{itemCount(6265, >2)} # Has any soul shards (Warlock)
@player{itemCount(8544, >0)} # Has any Mageweave Bandages-
canShiftInto(target)- player has enough mana to shift into target form and use ability (Druid)target: druid form keyword (aquatic,bear,cat,humanoid,moonkin,travel)- if current form equals target form: only checks ability mana cost
- if forms differ: checks target form cost + ability cost
-
canShiftBackFrom(target)- player has enough mana for round-trip: shift to target form, use ability, and return to current form (Druid)target: druid form keyword (aquatic,bear,cat,humanoid,moonkin,travel)- if current form equals target form: only checks ability mana cost
- if forms differ: checks target form cost + ability cost + current form re-entry cost
-
druidForm(target) -
rageSafe- player can switch stances without losing rage (Warrior)
@player{canShiftInto(bear)}
@player{canShiftBackFrom(bear)}
@player{druidForm(bear)}
@player{rageSafe}usable(infer)- player's inferred ability is usable
@player{usable(infer)}happy- pet is happy
@pet{!happy}