---
title: Particles
type: docs
weight: 1
---

The particle system in Hytale provides rich visual effects through configurable emitters and renderers. Create explosions, trails, auras, and complex visual effects with precise control over behavior and appearance.

**Package:** `com.hypixel.hytale.server.core.asset.type.particle`

{{< cards cols="3" >}}
  {{< card link="#spawning-particles" title="Spawning" subtitle="Create particle effects" icon="sparkles" >}}
  {{< card link="#particle-system-configuration" title="Configuration" subtitle="System and spawner setup" icon="adjustments" >}}
  {{< card link="#particle-attractors" title="Attractors" subtitle="Forces and movement" icon="arrow-circle-right" >}}
{{< /cards >}}

---

## Spawning Particles

### ParticleUtil Methods

{{< tabs items="Basic,Advanced,WorldParticle" >}}
{{< tab >}}

```java
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;

// Basic spawn at position (auto-finds nearby players)
ParticleUtil.spawnParticleEffect(
    "explosion_small",           // Particle system ID
    position,                    // Vector3d position
    componentAccessor
);

// Spawn for specific players
ParticleUtil.spawnParticleEffect(
    "magic_trail",
    position,
    playerRefs,                  // List<Ref<EntityStore>>
    componentAccessor
);
```

**Broadcast Distance:** `DEFAULT_PARTICLE_DISTANCE = 75` blocks

{{< /tab >}}
{{< tab >}}

```java
// With source entity reference
ParticleUtil.spawnParticleEffect(
    "attack_swing",
    position,
    sourceEntityRef,             // Entity that spawned this
    playerRefs,
    componentAccessor
);

// With rotation control
ParticleUtil.spawnParticleEffect(
    "directional_beam",
    position,
    yaw, pitch, roll,            // Rotation angles
    sourceEntityRef,
    playerRefs,
    componentAccessor
);

// With scale and color
ParticleUtil.spawnParticleEffect(
    "colored_burst",
    position,
    yaw, pitch, roll,
    2.0f,                        // Scale multiplier
    new Color(255, 100, 50, 255), // RGBA color
    playerRefs,
    componentAccessor
);
```

{{< /tab >}}
{{< tab >}}

```java
import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle;

// Full control with WorldParticle wrapper
WorldParticle worldParticle = new WorldParticle(
    "my_particle_system",
    new Color(255, 100, 50, 255),  // RGBA color override
    1.5f,                          // Scale multiplier
    new Vector3f(0, 1, 0),         // Position offset
    new Direction(0, 0, 0)         // Rotation offset
);

ParticleUtil.spawnParticleEffect(
    worldParticle,
    position,
    sourceRef,
    playerRefs,
    componentAccessor
);
```

{{< /tab >}}
{{< /tabs >}}

---

## Particle System Configuration

### ParticleSystem Class

The root configuration for a particle effect:

```java
public class ParticleSystem {
    protected String id;                        // Unique identifier
    protected ParticleSpawnerGroup[] spawners;  // Spawner configurations
    protected float lifeSpan;                   // System lifetime (seconds)
    protected float cullDistance;               // Distance before culling
    protected float boundingRadius;             // Collision bounding box
    protected boolean isImportant;              // Network priority flag
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | String | Unique identifier for the particle system |
| `spawners` | ParticleSpawnerGroup[] | Array of spawner configurations |
| `lifeSpan` | float | How long the system lives (seconds) |
| `cullDistance` | float | Distance at which particles are culled |
| `boundingRadius` | float | Bounding sphere for culling calculations |
| `isImportant` | boolean | If true, prioritized in network sync |

### ParticleSpawner Configuration

Controls how individual particles are emitted:

{{< tabs items="Properties,Emission,Movement,Rendering" >}}
{{< tab >}}

```java
public class ParticleSpawner {
    protected String id;
    protected EmitShape shape;
    protected RangeVector3f emitOffset;
    protected boolean useEmitDirection;
    protected Range totalParticles;
    protected float lifeSpan;
    protected int maxConcurrentParticles;
    protected Rangef particleLifeSpan;
    protected Rangef spawnRate;
    protected boolean spawnBurst;
    protected InitialVelocity initialVelocity;
    protected ParticleAttractor[] attractors;
    protected FXRenderMode renderMode;
    protected float lightInfluence;
    protected Particle particle;
}
```

{{< /tab >}}
{{< tab >}}

**Emission Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `shape` | EmitShape | Sphere or Cube emission shape |
| `emitOffset` | RangeVector3f | Random offset range from origin |
| `useEmitDirection` | boolean | Use spawn direction for velocity |
| `totalParticles` | Range | Min/max total particles to emit |
| `spawnRate` | Rangef | Particles per second |
| `spawnBurst` | boolean | Emit all at once vs over time |

**Example emission setup:**
```java
// Burst explosion - all at once
spawner.setSpawnBurst(true);
spawner.setTotalParticles(new Range(50, 100));

// Continuous stream
spawner.setSpawnBurst(false);
spawner.setSpawnRate(new Rangef(10, 20)); // 10-20 per second
spawner.setLifeSpan(5.0f); // Emit for 5 seconds
```

{{< /tab >}}
{{< tab >}}

**Movement Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `initialVelocity` | InitialVelocity | Starting velocity configuration |
| `attractors` | ParticleAttractor[] | Forces applied to particles |

**Initial Velocity:**
```java
public class InitialVelocity {
    protected float speed;           // Base speed
    protected float speedVariance;   // Random variance
    protected Vector3f direction;    // Base direction
    protected float coneAngle;       // Spread angle (degrees)
}
```

{{< /tab >}}
{{< tab >}}

**Rendering Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `renderMode` | FXRenderMode | How particles are blended |
| `lightInfluence` | float | How much lighting affects particles |
| `particle` | Particle | Texture and animation config |

{{< /tab >}}
{{< /tabs >}}

---

## Emit Shapes

Control the volume from which particles spawn:

```java
public enum EmitShape {
    Sphere,     // Emit from spherical volume
    Cube        // Emit from cubic volume
}
```

{{< tabs items="Sphere,Cube" >}}
{{< tab >}}

**Spherical Emission:**

Particles spawn within a spherical volume, with direction pointing outward from center:

```java
// Configure spherical emission
spawner.setShape(EmitShape.Sphere);
spawner.setEmitOffset(new RangeVector3f(
    new Rangef(-1, 1),  // X range (radius)
    new Rangef(-1, 1),  // Y range (radius)
    new Rangef(-1, 1)   // Z range (radius)
));

// Use emit direction for outward velocity
spawner.setUseEmitDirection(true);
```

Best for: Explosions, bursts, radial effects

{{< /tab >}}
{{< tab >}}

**Cubic Emission:**

Particles spawn within an axis-aligned box volume:

```java
// Configure cubic emission
spawner.setShape(EmitShape.Cube);
spawner.setEmitOffset(new RangeVector3f(
    new Rangef(-2, 2),  // X range
    new Rangef(0, 3),   // Y range (above ground)
    new Rangef(-2, 2)   // Z range
));
```

Best for: Area effects, rain, ground effects

{{< /tab >}}
{{< /tabs >}}

---

## Render Modes

Determine how particles blend with the scene:

```java
public enum FXRenderMode {
    BlendLinear,   // Standard transparency blending
    BlendAdd,      // Additive blending (bright/glowing)
    Erosion,       // Erosion/dissolve effect
    Distortion     // Distortion/refraction effect
}
```

{{< tabs items="BlendLinear,BlendAdd,Erosion,Distortion" >}}
{{< tab >}}

### BlendLinear

Standard alpha blending. Best for:
- Smoke
- Dust
- Clouds
- Solid-looking particles

```java
spawner.setRenderMode(FXRenderMode.BlendLinear);
```

{{< /tab >}}
{{< tab >}}

### BlendAdd

Additive blending - particles add light to the scene. Best for:
- Fire
- Sparks
- Magic effects
- Glowing particles
- Light beams

```java
spawner.setRenderMode(FXRenderMode.BlendAdd);
```

{{< callout type="info" >}}
Use `BlendAdd` for any glowing or luminous effects. Multiple overlapping particles will create brighter spots.
{{< /callout >}}

{{< /tab >}}
{{< tab >}}

### Erosion

Creates dissolving/eroding visual effect. Best for:
- Disintegration
- Dissolve transitions
- Energy dissipation

```java
spawner.setRenderMode(FXRenderMode.Erosion);
```

{{< /tab >}}
{{< tab >}}

### Distortion

Refracts the background, creating heat-shimmer effects. Best for:
- Heat waves
- Portals
- Energy fields
- Underwater effects

```java
spawner.setRenderMode(FXRenderMode.Distortion);
```

{{< callout type="warning" >}}
Distortion effects are more GPU-intensive. Use sparingly.
{{< /callout >}}

{{< /tab >}}
{{< /tabs >}}

---

## Particle Attractors

Apply forces to particles for dynamic movement:

```java
public class ParticleAttractor {
    protected Vector3f position;                // Local attractor position
    protected Vector3f radialAxis;              // Radial force direction
    protected float radius;                     // Influence radius

    // Accelerations (continuous forces)
    protected float radialAcceleration;         // Outward/inward acceleration
    protected float radialTangentAcceleration;  // Tangential (orbit) acceleration
    protected Vector3f linearAcceleration;      // Direct linear acceleration

    // Impulses (one-time forces)
    protected float radialImpulse;              // Outward/inward impulse
    protected float radialTangentImpulse;       // Tangential impulse
    protected Vector3f linearImpulse;           // Direct linear impulse

    // Damping
    protected Vector3f dampingMultiplier;       // Velocity reduction per frame
}
```

### Force Types

{{< tabs items="Radial,Tangential,Linear,Damping" >}}
{{< tab >}}

**Radial Forces:**

Push particles toward or away from the attractor position:

```java
ParticleAttractor attractor = new ParticleAttractor();
attractor.setPosition(new Vector3f(0, 0, 0)); // Center of system

// Outward explosion
attractor.setRadialAcceleration(10.0f);  // Positive = outward

// Inward pull (black hole effect)
attractor.setRadialAcceleration(-5.0f);  // Negative = inward

// Instant outward burst
attractor.setRadialImpulse(20.0f);
```

{{< /tab >}}
{{< tab >}}

**Tangential Forces:**

Create swirling/orbiting motion around the attractor:

```java
ParticleAttractor attractor = new ParticleAttractor();
attractor.setPosition(new Vector3f(0, 0, 0));
attractor.setRadialAxis(new Vector3f(0, 1, 0)); // Orbit around Y axis

// Clockwise swirl
attractor.setRadialTangentAcceleration(5.0f);

// Counter-clockwise
attractor.setRadialTangentAcceleration(-5.0f);
```

{{< /tab >}}
{{< tab >}}

**Linear Forces:**

Apply constant directional force (like gravity or wind):

```java
ParticleAttractor attractor = new ParticleAttractor();

// Gravity (downward)
attractor.setLinearAcceleration(new Vector3f(0, -9.8f, 0));

// Wind (horizontal)
attractor.setLinearAcceleration(new Vector3f(2.0f, 0, 0));

// Upward buoyancy
attractor.setLinearAcceleration(new Vector3f(0, 3.0f, 0));
```

{{< /tab >}}
{{< tab >}}

**Damping:**

Slow particles down over time:

```java
ParticleAttractor attractor = new ParticleAttractor();

// Uniform damping (air resistance)
attractor.setDampingMultiplier(new Vector3f(0.98f, 0.98f, 0.98f));

// Strong horizontal damping, weak vertical
attractor.setDampingMultiplier(new Vector3f(0.9f, 0.99f, 0.9f));

// No damping (particles maintain velocity)
attractor.setDampingMultiplier(new Vector3f(1.0f, 1.0f, 1.0f));
```

{{< callout type="info" >}}
Damping values < 1.0 slow particles. Values of 0.98-0.99 give realistic air resistance.
{{< /callout >}}

{{< /tab >}}
{{< /tabs >}}

---

## Particle Visual Configuration

Configure particle appearance and animation:

```java
public class Particle {
    protected String texture;                   // Texture atlas path
    protected Size frameSize;                   // Frame dimensions

    protected ParticleUVOption uvOption;        // None, Animated, Random
    protected SoftParticle softParticle;        // Soft particle blending
    protected float softParticlesFadeFactor;    // 0.1 to 2.0

    // Animation
    protected ParticleAnimationFrame initialAnimationFrame;
    protected Map<Integer, ParticleAnimationFrame> animation;
}
```

### Animation Options

```java
public enum ParticleUVOption {
    None,       // Single static frame
    Animated,   // Play through frames in sequence
    Random      // Random frame per particle
}
```

### Animation Frames

```java
public class ParticleAnimationFrame {
    protected int frame;           // Frame number in atlas
    protected Rangef scale;        // Size range
    protected Rangef alpha;        // Transparency range (0-1)
    protected Color color;         // Color tint
    protected Rangef rotation;     // Rotation range (degrees)
}
```

---

## Accessing Particle Assets

```java
// Get particle system by ID
ParticleSystem system = ParticleSystem.getAssetMap().getAsset("explosion_large");

// Get spawner configuration
ParticleSpawner spawner = ParticleSpawner.getAssetMap().getAsset("fire_spawner");

// Use in command arguments
// ArgTypes.PARTICLE_SYSTEM for command parameters
```

---

## Common Patterns

### Event-Based Particles

{{< callout type="warning" >}}
**Note:** The examples below are simplified. Entity doesn't have a direct `getPosition()` method. In real code, obtain position via `TransformComponent` from the entity's store. Example:
```java
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
Vector3d position = transform.getPosition();
```
{{< /callout >}}

{{< tabs items="Block Break,Combat,Death" >}}
{{< tab >}}

```java
@Subscribe
public void onBlockBreak(BreakBlockEvent event) {
    // Use getTargetBlock() - not getPosition()
    Vector3i blockPos = event.getTargetBlock();
    Vector3d pos = new Vector3d(
        blockPos.x + 0.5,
        blockPos.y + 0.5,
        blockPos.z + 0.5
    );

    // Spawn break particles at block center
    ParticleUtil.spawnParticleEffect(
        "block_break_particles",
        pos,
        componentAccessor
    );
}
```

{{< /tab >}}
{{< tab >}}

```java
@Subscribe
public void onEntityDamage(EntityDamageEvent event) {
    Entity target = event.getTarget();
    Vector3d hitPos = target.getPosition().add(0, 1, 0);

    // Blood/damage effect
    ParticleUtil.spawnParticleEffect(
        "damage_hit",
        hitPos,
        componentAccessor
    );

    // Critical hit special effect
    if (event.isCritical()) {
        ParticleUtil.spawnParticleEffect(
            "critical_hit_sparks",
            hitPos,
            0, 0, 0,    // rotation
            1.5f,       // larger scale
            new Color(255, 215, 0, 255), // gold color
            getNearbyPlayers(hitPos, 50),
            componentAccessor
        );
    }
}
```

{{< /tab >}}
{{< tab >}}

```java
@Subscribe
public void onEntityDeath(EntityDeathEvent event) {
    Entity entity = event.getEntity();
    Vector3d deathPos = entity.getPosition();

    // Death particles
    ParticleUtil.spawnParticleEffect(
        "entity_death_poof",
        deathPos,
        componentAccessor
    );

    // Soul rising effect for players
    if (entity instanceof Player) {
        ParticleUtil.spawnParticleEffect(
            "soul_ascend",
            deathPos.add(0, 0.5, 0),
            componentAccessor
        );
    }
}
```

{{< /tab >}}
{{< /tabs >}}

### Continuous Effects

{{< tabs items="Player Trail,Aura,Area Effect" >}}
{{< tab >}}

```java
// Spawn particle trail at player position
public void spawnTrailEffect(Player player, Store<EntityStore> store,
                              ComponentAccessor<EntityStore> componentAccessor) {
    if (hasTrailEffect(player)) {
        // Get position from TransformComponent
        Ref<EntityStore> entityRef = player.getReference();
        TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
        if (transform != null) {
            Vector3d position = transform.getPosition();
            ParticleUtil.spawnParticleEffect(
                "magic_trail",
                position,
                componentAccessor
            );
        }
    }
}
```

{{< callout type="warning" >}}
**Note:** Player doesn't have a `getPosition()` method. Get position via `TransformComponent` from the entity store.
{{< /callout >}}

{{< /tab >}}
{{< tab >}}

```java
// Persistent aura around player
public class AuraManager {
    private final Map<UUID, Boolean> activeAuras = new HashMap<>();
    private Store<EntityStore> store;
    private ComponentAccessor<EntityStore> componentAccessor;

    public void enableAura(Player player) {
        // Use player.getPlayerRef().getUuid() - not player.getUuid()
        activeAuras.put(player.getPlayerRef().getUuid(), true);
    }

    @Subscribe
    public void onTick(ServerTickEvent event) {
        for (Map.Entry<UUID, Boolean> entry : activeAuras.entrySet()) {
            if (entry.getValue()) {
                Player player = getPlayer(entry.getKey());
                if (player != null) {
                    // Get position from TransformComponent
                    Ref<EntityStore> entityRef = player.getReference();
                    TransformComponent transform = store.getComponent(
                        entityRef, TransformComponent.getComponentType());
                    if (transform != null) {
                        Vector3d pos = transform.getPosition();
                        ParticleUtil.spawnParticleEffect(
                            "magic_aura",
                            new Vector3d(pos.getX(), pos.getY() + 1, pos.getZ()),
                            componentAccessor
                        );
                    }
                }
            }
        }
    }
}
```

{{< /tab >}}
{{< tab >}}

```java
// Area healing effect
public void createHealingZone(Vector3d center, double radius, int durationTicks) {
    // Spawn ring particles
    for (int i = 0; i < 16; i++) {
        double angle = (2 * Math.PI * i) / 16;
        Vector3d pos = center.add(
            Math.cos(angle) * radius,
            0.1,
            Math.sin(angle) * radius
        );

        ParticleUtil.spawnParticleEffect(
            "healing_sparkle",
            pos,
            componentAccessor
        );
    }

    // Central pillar effect
    ParticleUtil.spawnParticleEffect(
        "healing_pillar",
        center,
        componentAccessor
    );
}
```

{{< /tab >}}
{{< /tabs >}}

---

## Performance Guidelines

{{< callout type="warning" >}}
**Performance Notes:**
- Default broadcast distance: **75 blocks** (`DEFAULT_PARTICLE_DISTANCE`)
- Particles use `SpawnParticleSystem` packet (ID: 152)
- Set `isImportant = true` for critical visual feedback
- Limit particle count for client performance
- Use `maxConcurrentParticles` to cap active particles
{{< /callout >}}

### Optimization Tips

| Tip | Description |
|-----|-------------|
| Limit total particles | Keep under 100-200 for burst effects |
| Use appropriate `cullDistance` | Don't render particles too far away |
| Batch spawns | Spawn multiple particles in same tick |
| Use `spawnBurst` wisely | Bursts are cheaper than continuous |
| Consider player count | More players = more network traffic |

```java
// Good: Efficient particle spawning
public void spawnEfficiently(Vector3d position) {
    // Only spawn for nearby players
    List<PlayerRef> nearbyPlayers = getNearbyPlayers(position, 50);

    if (!nearbyPlayers.isEmpty()) {
        ParticleUtil.spawnParticleEffect(
            "my_effect",
            position,
            nearbyPlayers,  // Limited audience
            componentAccessor
        );
    }
}
```

---

## Related Topics

- [Entity Effects]({{< relref "entity-effects" >}}) - Status effects with visual components
- [Networking]({{< relref "/advanced/networking" >}}) - How particles are broadcast
- [Events]({{< relref "/core-concepts/events" >}}) - Triggering particles from events
