The DS3 Codex
Chapter V

Effects

Flames, mist, splashes and the warm glow of Estus are all FXR3 effects: trees of containers whose actions spawn billboards, models and lights. Here's how they're stored, how a footstep becomes a ripple, and how DS3's particle shader adds them to the frame.

Three rings leave your feet with every step, yet no eye sees them. Only the water knows, and it bends where they pass.Codex · f000120001

In short
  • An effect (fNNNNNNNNN.fxr, FXR3) is a tree of containers → effects → actions. Each action holds untyped fields and keyed, curve-able properties.
  • Effects start from four places: the map (SFX regions: mist), objects (ModelSfxParam: candles, torches), clips (TAE 96: the Estus Flask) and the floor (footsteps through FootSfxParam).
  • Water ripples while wading are particles of the foot effect, drawn into a hidden ripple map that bends the water, not onto the screen.
  • DS3's unlit particle shader squares the colour and premultiplies the texture by its alpha, so a particle adds light ∝ alpha³. Missing that made our heal glow 10-40× too bright.

1FXR3, the effect format

Effects live in sfx/frpg_sfxbnd_commoneffects_effect.ffxbnd and per-area bundles, with textures and model particles in _resource bundles. Properties carry a type (1-4 components) and an interpolation (constant, stepped, linear, curves). A keyed property is laid out as: the key count, a min and a max, the key times (0-1 of the life, or seconds of age), the values, then curve extras data. EvenTorset's fxr library, the modders' reference, names each action's fields per game fxr. These are the actions our importer reads:

ActionNameWhat we take
128 / 129NodeAttributes / ParticleAttributesHow long the emitter runs / a particle's life
131ParticleModifierSpeed, scale, colour
300PeriodicEmitterInterval, per emission, total
404 / 501BoxEmitterShape / CircularSpreadSpawn volume / cone angle
55 / 60ParticleAcceleration / ParticleSpeedGravity (positive pulls down) and acceleration
35StaticNodeTransformOffset and rotation
132SFXReferencePlay another effect (121002 → 120002 → 120001)
603BillboardExTexture, flipbook columns and frames, blend mode, size, colours over age, rotation and spin
605ModelA FLVER drawn as a particle (the Estus Flask)
609PointLightColour, intensity, radius, flicker

Most of what the effects look like is in their sprite sheets: flipbooks of 8 × 8 frames, shown here as they add up (colour × alpha on black):

2From a footstep to a ripple

The cliprun a000_020100 carries TAE 128 PlaySound with SoundType 8 "Floor Material Determined", sound 2001, at each footfall (0.233 s, 0.6 s)
The floorthe collision triangle under the feet, or the wading sheet the feet are in, gives a HitMtrlParam row (materialNameData % 100)
FootSfxParamrow = the floor's material, column = the kind of step (2001 / 1000 = 2, a run): effect 100000 + row × 1000 + column. Water (20-22) → 120001
The effecta droplet spray (3 particles 1/15 s apart, rising 5 cm at 0.44 m/s against a 2 m/s² pull) and three ripple rings flat on the surface, 0.25 → 1.5 m over 2 s, spinning ±11.25°/s
The ripple mapthe rings' fields2[1] is 1, so they're added into a half-size height map instead of the screen; the water's surface reads its slope and bends its waves (Renderer). Only the spray is a sprite
WhereHitMtrlParam FootEffectHeightType0 is 1 on every water row: on the liquid's surface, at the lower of the two foot bones
Ripples round the knight's feet in the stream
Wading in our engine: the rings exist only in the ripple map, and show as the reflection bending in arcs round the feet.
Close

DS3's chain and numbers. Until 2026-10-15 the importer scaled every emitter by eye, which hid two misread actions (131's speed taken as the life, 55's gravity as a launch speed). Read by name, the scale factors went away. Until 2026-10-16 the rings were drawn as glowing sprites; a capture of walking through water showed DS3 draws them into the ripple map. We only spawn the water rows; dry ground's dust looks too big at our reading of its size.

3Placed effects: the mist

The MSB places effects with SFX regions data. m40 has 18:

EffectName (translated)What
840040 ×7霧 室内 10×10 "fog, indoor, 10×10"Ground mist along the Cemetery's start: one particle every 0.667 s in a 10 × 1 × 10 m box, 5 m wide, living 10 s
833002 ×3霧_遠景用 "fog for distant views"A sparse bank in a 50 × 50 × 200 m box
840031 ×3ライトシャフト "light shaft"The shrine's shaft model (not read) and dust motes
840060 ×5蝋燭光源 "candle light source"A point light only
Mist on the cemetery path
The Cemetery's mist in our engine. Placed effects start with particles of every age already there, and fade where they meet the scene (soft particles, our settings).

4Effects on objects: candles and torches

An object's effects come from ModelSfxParam, row = model number × 1000 + the part's relative id, which lists up to ten (effect, dummy poly) pairs.

Candles · o391000

409 placed, 210 lit. Row 391000000 "Candle One" → effect 839010 on dummy 200: one 0.135 m pulsing billboard per wick, anchored at its bottom, plus a point light. The wick offsets only land on the wicks with X mirrored (DS3 is left-handed).

Torches · o004665

12 placed, 10 lit. Effect 800022: the fire flipbook, and a 609 light of radius 5 m that fades in over 5 s and flickers every 0.25-0.5 s between 0.875 and full.

Bonfires · o000100

No ModelSfxParam row. Effects 1010-1014 are the same flames with more emitters each. Which one a lit bonfire burns isn't traced; we use 1011 guess.

Light from an effect = colour² × alpha × 10, squared like the gparam's colours, and drawn as DS3 draws map lights.

5Effects from clips

TAE event 96 SpawnOneShotFFX: effect id, dummy poly, slot, then flags for ignoring the dummy's angle, following it and so on. The imported clips use 38 effects. The Estus Flask shows the parts:

ClipSpanEffectDummySlot
50110 start0.2-0.83 s301000 the flask (a model particle, s07002.flver)901 right hand1
50111 gulp0-1.0 s3010009011
50111 gulp0.27-1.0 s301002 the heal's glow8 (Spine2)−1
50112 end0-0.5 s3010009011

The flask carries across three clips without a break because they share slot 1 guess. The effect's frame was settled from a capture of DS3 drinking: in dummy 901's matrix the flask sits at Rz(90°)·Rx(180°), so DS3's effect frame is the dummy turned half round its up, Z forward, Y up. Our first two tries held the bottle upside down. In that capture the flask is tinted (1, 0.27, 0.01) × 5: the liquid glows orange not done.

6DS3's particle shader

DS3 draws each emitter as one tessellated draw of its particles as points. The pixel shader, read from DXVK's SPIR-V of a capture of the heal capture:

C = vertex colour (colour 1 × colour 3);  L = |C|;  n = C / L
t = texture;  a = clamp(t.a + TEXCOORD5, 0, 1)
if premultiplyAlpha:  t.rgb *= a
rgb   = L × cb0[9].rgb × (t.rgb × n)²          the colour's direction and the texture squared, its length kept
alpha = C.a × a × cb0[9].w × soft-depth fade     blend: src × srcAlpha + dst

A colour of (2.5, 1.56, 0.63) comes out (2.07, 0.81, 0.13) × t²: deep orange, not near-white. cb0[9].rgb is the emitter's multiplier × 0.65217 (where that constant comes from isn't known). One constant read 0 through the bindings but was on: RenderDoc's pixel history settled it.

Getting the heal glow right

At first ours looked like a flame with a flat bottom, and far too bright. There were four faults: (1) a "floor" fade meant for splashes cut off every puff's lower half; (2) misread actions made the puffs 1 m instead of 0.56 m and ran their flipbook in 0.28 s instead of 1 s; (3) the colour wasn't squared; (4) no premultiply, so they were 10-40× too bright. Measured against DS3's frame (the light the particle draws add to the HDR target), it was 40,000+ against DS3's 3,667; now it's 2,300-2,800.

Close

Unlit particles (fire, the heal) use DS3's shader. Lit ones (mist, splashes) keep ours. Not done: DS3's depth blend (particles pulled up to w = size/2 toward the camera, so the glow stays on the body), random frame and scale, and frame blending.

7Hit effects: read, not done

victim material (NpcParam.materialSfx: hollows 110 "Flesh _ Weakness")
  → HitEffectSfxConceptParam[material].SfxConceptId[attack material]   → 210 "Blood (dead) _ Front"
  → HitEffectSfxParam row: concept + attack direction                  → HitSfxId[attack power]
  → effect 221401 in the common bundle

The blood effects play child effects of six dark red flipbook billboards. Spawned at the hit point, they sat inside the victim's body. The spray must come from actions we don't read yet. Blood and sparks aren't done.