Skip to content
Development
Agent

combat-visual-effects

Custom GLSL filters for hit effects, glow chaining, DisplacementFilter shockwaves, Spine integration, particle emitter combat patterns, z-ordering, and alpha masking in PixiJS v8+

From plugin
vexjoy-agent
443198 skills198 agents12 commands83 hooks

Claude Code

$ npx -y skills add notque/vexjoy-agent --agent claude-code

Agent definition

combat-visual-effects.md
description: Custom GLSL filters for hit effects, glow chaining, DisplacementFilter shockwaves, Spine integration, particle emitter combat patterns, z-ordering, and alpha masking in PixiJS v8+
agent: pixijs-combat-renderer
category: visual-techniques
version_range: "PixiJS v8+"

Combat Visual Effects Reference

> **Scope**: PixiJS v8 visual effects for combat rendering. Performance/batching in pixi-performance.md.

---

PIXI.Filter Custom GLSL for Hit Effects

import { Filter } from 'pixi.js';

const hitFlashVert = `
  attribute vec2 aPosition;
  varying vec2 vTextureCoord;
  uniform vec4 inputSize;
  uniform vec4 outputFrame;

  vec4 filterVertexPosition() {
    vec2 position = aPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;
    return vec4((position / (inputSize.xy / inputSize.zw)) * 2.0 - 1.0, 0.0, 1.0);
  }

  void main() {
    gl_Position = filterVertexPosition();
    vTextureCoord = aPosition * (outputFrame.zw * inputSize.zw);
  }
`;

const hitFlashFrag = `
  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  uniform float uFlash;  // 0.0 = normal, 1.0 = full white

  void main() {
    vec4 sample = texture2D(uSampler, vTextureCoord);
    gl_FragColor = vec4(mix(sample.rgb, vec3(1.0), uFlash * sample.a), sample.a);
  }
`;

export class HitFlashFilter extends Filter {
  constructor() {
    super({ glProgram: { vertex: hitFlashVert, fragment: hitFlashFrag } });
    this.resources.uniforms = { uFlash: 0.0 };
  }
  get flash(): number { return this.resources.uniforms.uFlash; }
  set flash(v: number) { this.resources.uniforms.uFlash = v; }
}

// Animate via GSAP
gsap.to(hitFilter, {
  flash: 1.0, duration: 0.06, yoyo: true, repeat: 1,
  onComplete: () => { characterSprite.filters = []; },
});

---

BlurFilter Chaining for Glow

import { AdvancedBloomFilter } from '@pixi/filter-advanced-bloom';

const bloomFilter = new AdvancedBloomFilter({
  threshold: 0.4, bloomScale: 0.8, brightness: 1.2, kernelSize: 5, quality: 4,
});
characterContainer.filters = [bloomFilter];

// Manual two-pass glow (no external package)
function addManualGlow(target: Sprite, glowColor: number, blur = 12): Container {
  const wrapper = new Container();
  const glow = Sprite.from(target.texture);
  glow.tint = glowColor;
  glow.alpha = 0.7;
  glow.filters = [new BlurFilter({ strength: blur })];
  glow.anchor.set(target.anchor.x, target.anchor.y);
  wrapper.addChild(glow);
  wrapper.addChild(target);
  return wrapper;
}

---

DisplacementFilter for Shockwave

export async function createShockwave(stage: Container, x: number, y: number): Promise<void> {
  const dispTexture = await Assets.load('/textures/displacement_circle.png');
  const dispSprite = new Sprite(dispTexture);
  dispSprite.anchor.set(0.5);
  dispSprite.position.set(x, y);
  dispSprite.scale.set(0.1);
  stage.addChild(dispSprite);

  const displacementFilter = new DisplacementFilter({ sprite: dispSprite, scale: { x: 80, y: 80 } });
  stage.filters = stage.filters ? [...stage.filters, displacementFilter] : [displacementFilter];

  gsap.to(dispSprite.scale, { x: 3.0, y: 3.0, duration: 0.6, ease: 'power2.out' });
  gsap.to(displacementFilter.scale, {
    x: 0, y: 0, duration: 0.6, ease: 'power3.in',
    onComplete: () => { stage.filters = stage.filters!.filter(f => f !== displacementFilter); dispSprite.destroy(); },
  });
}

---

Spine Animation Integration

import { Spine as PixiSpine } from '@esotericsoftware/spine-pixi-v8';

async function loadSpineCharacter(key: string): Promise<PixiSpine> {
  await Assets.load([
    { alias: `${key}-skel`, src: `/spine/${key}.skel` },
    { alias: `${key}-atlas`, src: `/spine/${key}.atlas` },
  ]);
  return PixiSpine.from({ skeleton: `${key}-skel`, atlas: `${key}-atlas` });
}

class SpineCombatCharacter {
  private spine: PixiSpine;
  private state: string = 'idle';

  constructor(spine: PixiSpine) {
    this.spine = spine;
    this.spine.state.setAnimation(0, 'idle', true);
  }

  attack(): void {
    if (this.state === 'hit') return;
    this.state = 'attack';
    this.spine.state.setAnimation(0, 'attack', false);
    this.spine.state.addAnimation(0, 'idle', true, 0);
    this.spine.state.addListener({
      complete: (entry) => { if (entry.animation?.name === 'attack') this.state = 'idle'; },
    });
  }

  takeHit(): void {
    this.state = 'hit';
    this.spine.state.setAnimation(1, 'hit', false);
    this.spine.state.addAnimation(1, null!, false, 0);
    setTimeout(() => { this.state = 'idle'; }, 300);
  }
}

---

Particle Emitter — Impact Burst

function burstImpact(stage: Container, x: number, y: number): void {
  const particleContainer = new Container();
  particleContainer.position.set(x, y);
  stage.addChild(particleContainer);

  const emitter = new Emitter(particleContainer, {
    lifetime: { min: 0.2, max: 0.5 }, frequency: 0.001, spawnChance: 1,
    particlesPerWave: 12, maxParticles: 12, pos: { x: 0, y: 0 },
    behaviors: [
      { type: 'alpha', config: { alpha: { list: [{ value: 1, time: 0 }, { value: 0, time: 1 }] } } },
      { type: 'scale', config: { scale: { list: [{ value: 0.6, time: 0 }, { value: 0.1, time: 1 }] } } },
      { type: 'color', config: { color: { list: [{ value: 'ff6600', time: 0 }, { value: 'ff2200', time: 0.3 }, { value: '330000', time: 1 }] } } },
      { type: 'moveSpeedStatic', config: { min: 150, max: 400 } },
      { type: 'rotationStatic', config: { min: 0, max: 360 } },
    ],
  });
  emitter.emit = true;
  setTimeout(() => { emitter.emit = false; setTimeout(() => { emitter.destroy(); particleContainer.destroy(); }, 600); }, 50);
}

---

Z-Index and Sorting

// Strategy 1: sortableChildren + zIndex
const stage = new Container();
stage.sortableChildren = true;
background.zIndex = 0; player.zIndex = 10; hitEffect.zIndex = 20; uiContainer.zIndex = 100;

// Strategy 2: explicit layer containers (faster — no sort)
const bgL
Read more
@notque
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.

44363 skills198 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
443
Star history of notque/vexjoy-agent on GitHub, a chart by star-history.com

Other agents on vexjoy-agent.

modules

modules

Agent

**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes…

@notque@notqueInstall Agent
testing

testing

Agent

**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**:…

@notque@notqueInstall Agent
communication-patterns

communication-patterns

Agent

**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what…

@notque@notqueInstall Agent

More agents from other plugins.

a11y-architect
Auto-invoked

a11y-architect

Agent

Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI…

ecc12d293k
@affaan-m@affaan-mInstall Agent
analyze-pass-impact

analyze-pass-impact

Agent

Analyzes how a specific topic affects a group of compiler passes. Used by the /plan-update skill to parallelize research across…

react1d252k
@facebook@facebookInstall Agent
orchestrator

orchestrator

Agent

- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as…

codex1d135k
@openai@openaiInstall Agent
cavecrew-builder

cavecrew-builder

Agent

Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…

caveman2d119k
@juliusbrussee@juliusbrusseeInstall Agent

Questions about combat-visual-effects.

Which coding agents can use combat-visual-effects?
combat-visual-effects installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.