Skip to content

GI Systems & Flasher Techniques

General Illumination (GI) and flasher systems are the two pillars of pinball table lighting. GI provides the warm ambient glow from dozens of bulbs under the playfield, while flashers deliver the dramatic punctuation -- bright bursts that highlight game events. Getting both right is essential for a convincing virtual pinball experience.

Quick Start

  1. GI strings: Trace wire colors in under-playfield photos, then verify with video frame-stepping (,/. keys in YouTube) and ROM GI test menus
  2. Toolkit GI: Create per-string Blender collections (Grouped mode), with shadow-casting lights in separate Split collections. Render all GI as pure white -- apply color in VPX script
  3. GI color temperature: Use 2700K with AGX/Tony McMapFace tonemapper (3000-3200K if using Filmic)
  4. Flasher fading: Use the cubic decay pattern: FlashLevel = FlashLevel * 0.93 - 0.01, then flashx3 = FlashLevel^3. Each flasher gets its own SolCallback + timer
  5. Room brightness: Always bake with room lighting ON, then darken in script via SetRoomBrightness. You cannot brighten beyond the baked level
  6. Incandescent fade speeds: Fade up 1/40, fade down 1/120 (asymmetric -- filaments heat faster than they cool). LEDs: up 1/2, down 1/8
  7. GI numbering gotcha: String 1 = gi0lvl, String 2 = gi1lvl (off-by-one from hardware)

Contents


GI String Organization and Tracing

Identifying GI Strings from Hardware

GI string assignments are notoriously undocumented. Unlike lamps and solenoids which appear in machine schematics, GI wiring must be determined through investigation. Sixtoe expressed this frustration on Medieval Madness: "There are never any GI diagrams, it's infuriating."

The methodology refined across many builds, most thoroughly documented during Johnny Mnemonic:

  1. Wire color tracing: Examine high-resolution under-playfield photos and trace wire colors to identify which bulbs share a circuit. Caveat: switch matrix wires can use similar colors (white-green was used for both GI string 5 and one switch matrix row on JM).

  2. Video frame-stepping: Use the , and . keys in YouTube to step frame-by-frame through attract mode or ball launch sequences where GI strings cycle independently. JM showed a 3-2-1-3-2-1 pattern that revealed the alternating string assignments.

  3. Real machine GI test: Access the ROM's built-in GI string test menu on a physical machine. Iaakki went to an arcade specifically to capture this test on video for JM, providing the definitive baseline.

  4. Cross-reference multiple machines: Account for blown bulbs. Single-machine footage may show gaps that look like string boundaries but are actually dead bulbs.

  5. Circuit diagram analysis: Williams schematics show GI string wiring but require patience to decode.

On Black Rose, sixtoe discovered that the manual's "top" and "bottom" labels refer to physical bulb position (red above white in each pair), NOT top/bottom of the playfield -- a common misinterpretation that caused incorrect GI assignments. On Diner, cross-referencing manual diagrams with the physical lamp layout was required because manuals sometimes have errors in lamp numbering. On T2, the manual listed GI assignments twice with different information -- the second listing was correct.

GI String Numbering Gotcha

GI string numbering is off-by-one from the GI channel index -- String 1 maps to gi0lvl, String 2 to gi1lvl, and so on. First documented on Black Rose, this mismatch is a common source of bugs. Always verify the mapping between hardware string numbers and the software channel indices.

Unusual GI Configurations

Not all tables follow the standard multi-zone dimmable GI pattern:

  • SAM (Stern) GI is a single on/off relay, not dimmable zones, confirmed by djrobx during the Spider-Man build. Don't try to implement per-zone dimming on SAM tables.
  • Data East GI uses inverted solenoid logic on some tables -- the solenoid going high turns GI off, not on. This caused confusion during Guns N' Roses debugging.
  • System 11 GI on tables like Police Force has a "GI Blink" solenoid that works inversely to normal flashers -- it dims GI when activated, using a TIP102 transistor.
  • SAM lamp matrix GI: On Stern SAM tables like Metallica, GI strings are driven by lamp matrix indices (130, 132, 134, 136) rather than through the standard GI callback.

GI Shaped Lights vs Point Lights

Pre-Toolkit GI (Shaped Flashers)

Before the VPX Lightmapper toolkit, GI was typically implemented using playfield-sized additive flasher primitives. Each GI string gets a baked flasher texture rendered in Blender/Octane with only that string's bulbs lit. At runtime, flasher opacity tracks GI level. First used extensively on Indiana Jones and documented on Simpsons Pinball Party, this approach is performant and visually consistent.

On Bride of Pinbot, GI brightness was controlled via opacity on playfield-sized flasher objects: FGIFront.opacity = 300 * GIFrontLvl and FGIRear.opacity = 2000 * GIRearLvl.

Toolkit GI (Lightmaps)

The VPX Lightmapper toolkit generates per-light or per-string lightmap meshes that overlay the playfield and parts. Each lightmap primitive's opacity is automatically controlled by its linked VPX light object.

Toolkit light collection configuration, detailed on Haunted House:

  • Grouped lights: All lights in a collection with the same VPX Name are grouped into a single lightmap. Good for GI strings where multiple bulbs act as one unit.
  • Split lights: Check the "Split" checkbox to give each light its own individual lightmap. Required for GI lights that should cast independent ball shadows.

For Spongebob (original table), each GI string requires its own Blender collection with the lightmapper property set to "split," and all lights within a single string must share the same VPX light name.

Insert Dual-Bulb Setup in Blender

Each insert in the toolkit requires two lights in Blender but only one light in VPX, as documented on Haunted House and Godzilla (Sega):

  1. Inside the insert: Models the light bulb below the playfield
  2. Slightly above the playfield: Models the light spread/bloom that Blender doesn't automatically handle well

Without the top spread light, inserts look too dark and lack playfield diffusion. Niwak noted during the Godzilla build that the deep insert light alone has too narrow a shadow cone to create realistic surface spread.

GI ON/OFF Fading

Pre-Toolkit: Doubled Primitive System

The pre-toolkit approach to GI fading uses paired ON and OFF primitives for each visual element, first documented on Tales from the Crypt and refined on Indiana Jones.

Each physical object (plastic, cabinet side, ramp) has two copies with different baked lighting. The script toggles visibility and cross-fades opacity between them using UpdateMaterial. Key settings from TFTC:

  • ON primitives: Layer 7, depth bias 0, static rendering unchecked
  • OFF primitives: Layer 9, depth bias 30, static rendering unchecked

The depth bias separation must be adequate to prevent z-fighting. Both primitives must have static rendering unchecked for runtime visibility changes to work.

Critical discovery on Indiana Jones: Normal maps on ON primitives break UpdateMaterial in VR -- the material update silently fails. Fix: remove normal maps from any primitive that needs runtime opacity changes. Desktop mode works fine; the issue is VR-specific.

Per-Material Fade Curves

Different materials respond differently to ambient light changes, and a universal fade looks unrealistic. Per-material-type fade curve exponents were established during the TFTC build by iaakki:

  • Cabinet/wood: aLvl^5 -- dramatic darkening
  • Plastics: aLvl^3 -- moderate fade
  • Metal parts: aLvl^2 -- subtle fade
  • Bulb glass: aLvl^0.5 -- minimal fade (glass transmits light)

These values were tuned against real machine GI dimming behavior. The same approach was used on Judge Dredd and Hook.

Toolkit: Lampz/ModLampz System

The modern approach uses the Lampz (or ModLampz) system for automated GI fading. Each GI string is registered with Lampz, which handles fade speed and intensity curves internally.

Lampz FadeSpeed values by bulb type, documented by niwak during the Iron Man build:

  • LEDs (fast response): Fade up 1/2, fade down 1/8
  • Incandescent bulbs (thermal lag): Fade up 1/40, fade down 1/120

The asymmetric up/down speeds for incandescents reflect the physical reality that filaments heat faster than they cool.

Lampz.state() must be used instead of LampState() for lampz-enabled tables, as documented on No Fear. The lampz system provides more accurate light state tracking that integrates with PWM dimming.

Important limitation: ModLampz internal level values cannot be reliably overridden at runtime from outside the lampz system, discovered during Black Rose. Writing to ModLampz.Lvl(n) may appear to work in the debugger but has no effect in-game because the lampz system overwrites external changes on the next update cycle.

VPX 10.8 Incandescent Fader

VPX 10.8 introduced built-in light fading with incandescent bulb simulation, documented by niwak on Johnny Mnemonic. Select the "incandescent" fader type on VPX light objects to get the correct nonlinear fading curve. Working values: 40ms up, 120ms down produced good visual results. Reference measurements showed approximately 40ms fade up/down for flashers.

GI Cross-Dimming

When a table has two GI strings of different colors that are normally both on simultaneously, the lights must be tuned dim enough to not blow out when combined. This makes single-color modes too dim.

The solution, first implemented on Black Rose (red + white GI strings): create a separate boost light collection controlled by the equation IntensityScale = (1 - gi2lvl) * gi1lvl, where gi2lvl is the white string level and gi1lvl is the red string level. This provides smooth fading: both on = boost off, red only = boost on, partial states = proportional boost.

The boost lights operate outside the ModLampz system using a separate VPX light collection controlled directly via IntensityScale in GI update callbacks.

Ball Brightness Tracking GI State

Ball brightness in VPX should dynamically respond to GI state rather than maintaining a constant unrealistic brightness. Niwak's approach, documented during the Iron Man build, calculates ball brightness based on GI opacity and distance to the nearest GI light source:

  • GI fully on (opacity=1): Ball at full brightness
  • GI dimmed/off: Ball brightness reduces proportionally
  • Distance factor: Ball farther from GI lights appears darker than one directly under a GI lamp

This creates realistic visual feedback where the ball dims when GI is off (e.g., during multiball or attract mode). The Day/Night slider also affects ball brightness but does NOT affect toolkit-baked primitive textures, so ball brightness may need separate tuning to match the overall scene lighting.

GI Ball Shadows

Per-String Shadow Control

For proper ball shadows under GI, lights that should cast ball shadows must be placed in "Split" collections in the toolkit, with the "cast ball shadows" checkbox set. Grouped GI lights should NOT cast ball shadows, as documented on Lethal Weapon 3.

Separate GI into per-string collections (grouped within each string), with lights needing independent ball shadows in their own Split collection. This approach from Haunted House provides per-light shadow accuracy without the performance cost of splitting every GI bulb.

Ball Reflection Lights

Adding VPX lights at approximately Z=52 units height around slingshots and high-traffic areas creates visible GI reflections on the ball as it passes through those zones. This "Skitso-style" technique from Dr. Who makes the ball appear illuminated by the table's own GI. The lights must be tied to GI strings so they respond to GI state changes. Actual VPX light objects are needed for ball reflections -- flasher textures do not produce ball reflections.

On Iron Maiden, ball reflection lights at halo height -5 (below playfield) must be coded to match the current GI state. If always off, ball reflections won't respond to GI. If always on, they conflict with GI-off states.

GI Color Mod System

Render White, Color in Script

The recommended approach is to render all GI lights as pure white in Blender, then apply color modifications in VPX script. First systematized on Haunted House and also used on Stargate, this provides unlimited color mod options without re-rendering. Users can choose presets or custom RGB colors for each GI group.

On Bride of Pinbot, iaakki implemented a comprehensive GI color mod system with 13 options selectable via a script variable: Incandescent, Warm White LED, Cool White LED, seven individual color LEDs, Custom RGB, and Auto-changing. Each mode sets RGB values applied to baked GI flasher primitives.

Caveat from iaakki (documented on Bad Cats): changing GI color via RGB adjustment is a "hack" that can ruin the original lighting bake. Adjustments should be subtle.

GI Color Temperature

Real pinball GI bulbs use incandescent bulbs at approximately 2700K color temperature (warm yellowish white), confirmed during Star Wars DE and Jurassic Park. With the Filmic tonemapper, 3000-3200K was needed because 2700K looked too yellow. With Tony McMapFace (and later AGX), 2700K -- the physically correct value -- works correctly.


The sections above cover GI -- the ambient lighting backbone. The sections below shift to flashers -- the event-driven punctuation lights that fire on solenoid commands.


Flasher Exponential Decay Pattern

The standard VPW flasher fading pattern uses exponential decay with a cubic function. The canonical code, first documented on Congo and refined on Metallica:

Sub SolFlash_Timer()
    FlashLevel = FlashLevel * 0.93 - 0.01
    flashx3 = FlashLevel * FlashLevel * FlashLevel
    Flasher.opacity = 110 * flashx3
    Light.IntensityScale = 3 * flashx3
    If FlashLevel < 0 Then FlasherTimer.Enabled = False
End Sub

The cubic function (FlashLevel^3) creates a fast initial decay that slows near zero, matching incandescent cooling. Each flasher solenoid gets its own SolCallback sub that sets FlashLevel = 1 and starts the timer.

Per-Element Variation

On Black Rose, iaakki implemented per-element variation to avoid the "Christmas tree" look where all flashers fire identically. Each flasher element gets slightly different fade speed, intensity, and RGB color shift during decay (shifts toward amber as intensity drops, simulating incandescent cooling).

Fade-Up for Incandescent Realism

Real flasher domes (especially on System 11 era games) don't reach full intensity instantly -- they ramp up slowly. When flickered rapidly by the ROM, they never reach maximum brightness. On Jokerz, iaakki reworked the dome code to include fade speed, with slight per-flasher speed differences to prevent all domes banging to max simultaneously.

Stacking Prevention

Flash fadedown speed set too low causes catastrophic performance issues, as discovered on Metallica. When fadedown is slow, rapid flasher fire events stack -- each new flash event starts before the previous fade completes, creating compound rendering load. The fix: increase fadedown speed so each flash completes before the next fires, or implement logic to cancel pending fades when a new flash event arrives.

A complementary technique from Simpsons Pinball Party: when multiple flashers fire simultaneously during multiball, a flasher sum limiter caps the total flasher contribution by proportionally scaling each flasher's intensity down so the total stays within bounds.

On Jokerz, iaakki used GI-aware flasher opacity: FlasherLL_L.opacity = (100 - 30*gilvl) * ObjLevel(nr). When GI is ON, max flasher opacity is 70; when OFF, max is 100. This prevents flashers from washing out the scene when combined with full GI.

Flasher Dome Integration (Flupper Domes)

Flupper's flasher dome system, first documented on F-14 Tomcat and refined on Jokerz, uses dome meshes with textures rendered from a specific orthographic angle and projected from the front.

Naming Requirements

The system requires precise naming of all lights and flashers. The script iterates objects by name pattern, so each flasher must use the specified naming convention or the code breaks. Each flasher has three "bloom" lights/flashers, and some blooms are VPX lights while others are VPX flashers, requiring dual flashlight arrays. Renaming objects to match the expected pattern is easier than rewriting the iteration code.

Clear Dome Construction

Creating transparent flasher domes (like F-14's ear-shaped flashers) requires splitting the dome mesh, as documented on F-14 Tomcat. Delete the back faces of the dome mesh so there is nothing to see through. The dome also needs a slightly larger duplicate mesh for the "lit" state that activates when the flasher fires. Each dome rotation in the VPX table must be handled via script, not mesh rotation, because the texture coordinates are fixed to face the camera.

Lit/Unlit Dual-Primitive

On Jokerz, the dome system uses two identical overlapping primitives: a base (always visible) and a lit version on top with higher depth bias. When the flasher activates, BlendDisableLighting values are ramped and the lit primitive's material opacity increases. Key detail from Flupper: ensure the base prim uses the "off" texture, not the "lit" texture -- otherwise the lit version bleeds through when the flasher should be dark.

BlendDisableLighting for Filaments

To make a physical bulb filament mesh glow when its flasher fires, tie BlendDisableLighting to the flasher's ObjLevel value. Flupper's one-liner solution from F-14 Tomcat:

pBulbTest.BlendDisableLighting = 25 * ObjLevel(14)

Added to the flasher timer sub, this makes the filament mesh brightness track the flasher fade. The multiplier (25) controls peak glow intensity.

Collection-Based BDL

To apply BlendDisableLighting to all objects in a VPX collection (e.g., GI bulb filament meshes), create a wrapper sub. From F-14 Tomcat:

Sub DisableLightingColl(coll, DLintensity, ByVal aLvl)
    For Each p In coll
        DisableLighting p, DLintensity, aLvl
    Next
End Sub

Register with Lampz: Lampz.Callback(110) = "DisableLightingColl ColTest, 25,".

SolCallback vs SolModCallback

SolCallback provides binary 0/1 values from solenoid events. SolModCallback provides modulated 0-255 values for solenoids that support pulse-width modulation. Not all tables or solenoids support modulated output. As documented on Congo:

Even with modulated solenoids, the actual output values during gameplay are unpredictable -- Congo's Sol17 showed erratic output levels (27, 72, 109, 36) with varying intervals (56ms to 3023ms). Best practice: use per-flasher timers with exponential decay regardless of modulation support, rather than driving flasher brightness directly from solenoid values.

For modulated solenoid flashers on 90s WPC tables like Cirqus Voltaire, the modulated solenoid value (0-255 from ROM) maps to opacity (0-100 in VPX). The maximum in-game modulated value may be lower than 255. Iaakki's technique: debug.print the values during actual gameplay to find the real maximum, then normalize to that value for full brightness response.

PWM Flasher Integration (PinMAME 3.6+)

PinMAME 3.6 introduced PWM (Pulse Width Modulation) for true-to-life lamp and flasher behavior: slow fades, quick flashes, simultaneous fade-and-blink effects. This was a major architectural change, first documented on Big Bang Bar (Capcom) and expanded to WPC tables.

Migration Steps

From the Godzilla (Sega) build:

  1. Set UseVPMModSol = 2 for new physics output (UseVPMModSol = 1 maintains backward compatibility)
  2. PWM signals from core.vbs are now float 0..1 range (previously 0..255)
  3. Remove SolMask array initializations
  4. Set light object fader to Incandescent for lamps, or None for flashers already receiving PWM modulation (can be done from script via the Fader property). See also Software Setup: PWM Migration
  5. Updating a lightmapped VPW table takes less than five minutes

Platform Differences

PWM requirements vary by platform, documented by niwak on Jurassic Park:

  • WPC: Obviously needs PWM for GI, solenoids, and sometimes lamps
  • Whitestar/SAM: Definitely needs it for everything
  • System 11 / Data East: More true to real hardware but the benefit is less obvious; GI uses relays (instant on/off)

Flipper Output PWM

Flashers wired to flipper solenoid outputs appeared as binary on/off rather than modulated. Niwak confirmed PWM was missing from Fliptronics/flipper outputs in PinMAME and added it after the Scared Stiff build -- Scared Stiff is the only WPC table with a flasher wired to a flipper output.

GI Relay Sounds with PWM

When tables use PWM, GI relay sounds need threshold-based triggering instead of on/off. Without thresholds, the relay sound plays on every PWM update, causing rapid ticking. From Godzilla (Sega):

If lvl < 0.01 And lvl <> lastLvl Then
    Sound_GI_Relay 0, Bumper1
ElseIf lvl > 0.99 And lvl <> lastLvl Then
    Sound_GI_Relay 1, Bumper1
End If

VPX Lightmap Property

VPX primitives have a "Lightmap" property that connects them to a VPX light object. When connected, the primitive's opacity is automatically controlled by the light's state -- no script code needed. This replaced the old approach of writing custom flasher fader subs for every flasher. Documented on Police Force:

  1. Create a VPX light object with correct timer interval matching the lamp/solenoid number
  2. Set the light's render mode to "Hidden" and fader model to "Incandescent" (or "LED" for flashers)
  3. In each lightmap primitive's properties, set the "Lightmap" field to point to that light

Works for flashers (via SolModCallback PWM), insert lamps, and backglass elements. For fine opacity control on non-lightmap primitives, use the _Animate sub:

Sub L61_Animate
    Dim f
    f = L61.GetInPlayIntensity / L61.Intensity
    Flasher61.opacity = 1000 * f
End Sub

The sections below cover the broader lighting environment: room brightness, BDL material control, and specialized techniques that tie GI and flasher systems together.


Room Brightness Control

For baked (lightmapped) tables, room brightness is controlled by modulating bakemap material base colors toward black. Documented on Godzilla (Sega):

  1. Define LightLevel variable (0-100 scale)
  2. Call SetRoomBrightness LightLevel/100 in Table1_Init
  3. The system saves original material base colors, then scales RGB channels toward black

Critical rule from Johnny Mnemonic: Room brightness can only darken from the baked level -- it cannot brighten beyond what was baked. If the batch was rendered without room lighting, the table will appear too dark with no way to brighten it in VPX. Always bake with room lighting ON, then darken in script.

Double-darkening can occur when both primitive.color and SetRoomBrightness are used simultaneously, as discovered on Creature from the Black Lagoon. Use only one method, preferably SetRoomBrightness.

DisableStaticPrerendering (VPX build 1666+, documented on Godzilla (Sega)) allows adjusting room brightness via the in-game options menu without requiring a table restart.

Drop Target Shadows via Flashers

Dynamic drop target shadows require additional renders of the shadow appearance, flasher objects placed over the dropped target area, and code to drive flasher visibility when targets drop below the playfield. Described during the Countdown build as "the least fun part of any table," the shadows are darkening flashers that simulate the visual hole where dropped targets sit below the playfield surface. This technique is especially tedious when a table has many drop target banks.

BlendDisableLighting for GI Elements

BlendDisableLighting (BDL) on VPX primitives controls how much the object ignores dynamic lighting -- effectively a brightness override. As documented on Diner by niwak, BDL=0 means fully lit by scene lights (darkest), BDL=1 means ignore lighting entirely (fullbright). Values above 1.0 can be set via script for even brighter objects (confirmed on Simpsons Pinball Party), though the editor slider caps at 1.0.

For bulb primitives to respond to BDL changes in script, they must be set to non-static in the VPX editor, as documented on Monster Bash. Static primitives ignore runtime material changes.

Negative values break rendering. Setting blenddisablelighting to negative values on VPX primitives causes colors to break -- inverted or washed-out colors, as documented on Metallica. Always clamp to the 0+ range.

Ramp DL Tied to GI

To make transparent ramps respond to GI lighting changes, tie their BDL to the GI lightmap opacity. From No Fear:

lramp.blenddisablelighting = 0.01 * LM_giTop_Parts.opacity

On Hook, ramp primitive BDL values were tied to GI level with minimum visibility constants:

Primitive_MainRamp.blenddisablelighting = gilevel * 0.12 + 0.03

The + 0.03 ensures ramps remain minimally visible when GI is off.

GI Overhaul Methodology

Skitso's approach, documented during Tron, is to strip all existing GI lighting to bare bones, then rebuild from scratch with consistent light colors, intensity levels, and falloff values. Layered modifications from multiple contributors result in conflicting light settings, ghost mods, and overexposed areas. A complete pass by one person produces better results than incremental adjustments by many.

On Road Show, Skitso described the workflow as: build GI from scratch, tackle individual elements one at a time (ramps, plastics, details), tweak texture and material and light-passing properties for each, then step back and evaluate the table as a whole. Repeat for four to five passes until the table looks right. There is no specific formula -- it is iterative and requires visual judgment at each step.

GI Bulb Placement

GI bulb light center points must be positioned at the physical top of the bulb, not adjusted to look correct from a specific camera angle. Moving light centers to match a POV causes them to appear incorrectly from any other angle, including VR. This was documented on Cactus Canyon -- the fix is to place bulb centers at physically correct positions and handle brightness through intensity values rather than position offsets.

Spotlight Subtlety

Real machine spotlights are far more subtle than VPX implementations typically show. Confirmed during the Tron build via multiple gameplay videos: no visible light cones, no visible shadow casting, just very faint ambient illumination on the playfield center. The spotlights are mounted high and aimed to provide general illumination rather than focused spots. For VPX, use soft ambient flashers rather than visible light cones or shadow-casting lights. Visible spotlight effects should be an option, not default.

Shadow-casting lights positioned above the playfield cause unnatural shadow movement in VR when the player moves their head. As documented on Tommy: "Shadow-casting lights dropped to playfield height in VR."

Day/Night Slider Behavior in Toolkit Tables

The Day/Night slider in VPX affects ball brightness and VPX light objects but does NOT affect primitive textures in toolkit-rendered tables, as documented on Fish Tales. This creates a disconnect where adjusting the slider changes the ball appearance and background lighting but the playfield and parts remain at their baked brightness level.

The slider still has utility for adjusting ball appearance and non-toolkit lighting elements. In VR rooms, DN slider compatibility needs separate handling. Tomate80 preferred total dark playfield (slider at 0%) for immersive lighting where only the baked toolkit lighting is visible.

System 11 Flasher Relay Sounds

System 11 machines use relays in the backbox for flashers, producing an audible click. As documented on Bad Cats, relay sounds are accurate for System 11 flashers -- confirmed via Williams schematics. Not all flasher outputs use relays; volume should be subtle as IRL the relay click is not very prominent. Different systems handle flasher outputs differently -- some use muxed solenoid outputs where relay sounds only occur for certain combinations.

Flasher Naming Convention

VPW standardized flasher naming on Police Force: lowercase "f" prefix + "1" prepended to the solenoid callback number. Example: solenoid callback 9 becomes f109, callback 25 becomes f125. The leading "1" prevents confusion by creating a fixed, absolute value distinct from lamp numbers.

Flashers that fire simultaneously can share the same VPX light name in the toolkit, reducing lightmap count. Documented on Last Action Hero: each group only needs one control light in VPX.

Flasher Solenoid Discovery

When flasher-to-solenoid mappings are not documented in the manual (common with Data East tables), the discovery method documented on Hook and CFTBL:

  1. Assign a test flasher to a candidate solenoid number
  2. Use manual ball control to drag ball over switch triggers
  3. Observe if the solenoid fires with correct timing
  4. Use vpmTimer.PulseSw <number> in the VPX editor to manually trigger switches and observe responses

On CFTBL, apophis79 and niwak dumped all solenoid outputs during attract mode to find undocumented backglass flashers on solenoids 33-36. On Last Action Hero, sixtoe derived complete flasher mapping from Data East schematics page 2, identifying relay-shifted solenoids and dual-function inserts.

Insert Brightness When GI Is Off

When GI turns off for dramatic effect, lit inserts should appear relatively brighter to simulate eye adaptation. This was first implemented by Flupper on F-14 Tomcat, where an insert lighting script includes a multiplier that increases insert IntensityScale when GI is off. The technique was refined on Tommy, where each light's base intensity was stored in its UserValue property because VBScript arrays were not accessible from Lampz callbacks due to initialization timing.

Flasher Texture Preloading

VPX can stutter the first time a flasher texture is displayed because the texture is loaded on demand rather than at table init. The workaround, documented on Cactus Canyon and Indiana Jones: briefly display each flasher texture during table initialization (with opacity too low to be visible) to force VPX to load and cache all textures. This eliminates the first-time stutter when flashers fire during gameplay.