Compatibility
Minecraft: Java Edition
Platforms
Supported environments
Links
Tags
Creators
Details
Using BreathWeapon
This guide explains how to integrate the BreathWeapon particle system into your Minecraft mod to create custom particles without writing complex rendering code.
Setup
1. Add Dependency
In your build.gradle, add BreathWeapon to your dependencies:
mavenCentral()
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
forRepositories(fg.repository)
filter {
includeGroup "maven.modrinth"
}
}
dependencies {
implementation fg.deobf("maven.modrinth:breath-weapon:0.0.1")
}
2. Import the API
In your code, import the spawning utilities:
import com.dracolich777.breathweapon.util.ParticleSpawner;
import com.dracolich777.breathweapon.api.particle.ParticleRegistry;
import net.minecraft.resources.ResourceLocation;
Quick Start
Minimal Setup
1. Create a particle definition JSON:
src/main/resources/data/yourmodid/particles/my_effect.json
{
"type": "sprite",
"lifetime": 50
}
2. Spawn it in code:
ParticleSpawner.spawn(level,
new ResourceLocation("yourmodid", "my_effect"),
x, y, z,
vx, vy, vz);
That's it! The particle will render with default texture and physics.
Creating Particle Definitions
Particle definitions are JSON files placed in your mod's datapacks. Create them at:
src/main/resources/data/yourmodid/particles/particle_name.json
File Structure
src/main/resources/
├── assets/yourmodid/
│ └── textures/
│ └── particle/
│ ├── beam.png
│ ├── spark.png
│ └── smoke.png
└── data/yourmodid/
└── particles/
├── my_beam.json
├── my_spark.json
└── my_smoke.json
Basic JSON Structure
Every particle definition has this structure:
{
"type": "sprite | beam | geo",
"texture": "yourmodid:textures/particle/name",
"lifetime": 200,
"scale": 1.0,
"gravity": 0.0,
"drag": 1.0,
"light": { ... },
"animation": { ... }
}
Common Properties
All particle types support:
| Property | Type | Default | Description |
|---|---|---|---|
type |
string | "sprite" | Particle type: sprite, beam, or geo |
lifetime |
int | 200 | Duration in ticks (1 tick = 50ms) |
scale |
float | 1.0 | Size multiplier (0.1 - 10.0) |
gravity |
float | 0.0 | Downward acceleration (negative = upward) |
drag |
float | 1.0 | Velocity damping (0.9 = 10% slowdown/tick) |
texture |
string | null | Texture path in format modid:path |
Light Configuration
Add glow/dynamic lighting to particles:
{
"type": "sprite",
"light": {
"mode": "glow",
"color": [1.0, 0.5, 0.0],
"intensity": 1.0
}
}
Light Modes:
"none"- No special lighting"glow"- Constant glow effect"pulse"- Pulsing brightness
For Glow/Pulse:
"light": {
"mode": "glow",
"color": [r, g, b],
"intensity": brightness_0_to_2
}
For Pulse:
"light": {
"mode": "pulse",
"color": [r, g, b],
"intensity": 1.0,
"pulse_min": 0.3,
"pulse_max": 1.0,
"pulse_speed": 0.1
}
Spawning Particles
Basic Spawning
Single particle:
ParticleSpawner.spawn(level,
new ResourceLocation("yourmodid", "my_particle"),
x, y, z,
velocityX, velocityY, velocityZ);
Pattern Spawning
Burst (random directions):
ParticleSpawner.burst(level,
new ResourceLocation("yourmodid", "spark"),
center, // Vec3 position
count, // Number of particles (e.g., 10)
speed); // Speed in blocks/tick (e.g., 0.5)
Stream (directional):
ParticleSpawner.stream(level,
new ResourceLocation("yourmodid", "smoke"),
entity, // Entity to spawn from
count, // Number of particles
speed, // Particle speed
spread); // Angular spread in radians
Beam Spawning
Uniform beam:
ParticleSpawner.spawnBeam(level,
new ResourceLocation("yourmodid", "laser"),
from, // Vec3 start position
to, // Vec3 end position
width); // Beam width (e.g., 0.3f)
Tapered beam (width changes along length):
ParticleSpawner.spawnBeam(level,
new ResourceLocation("yourmodid", "laser"),
from, // Vec3 start
to, // Vec3 end
startWidth, // Width at start (e.g., 0.5f)
endWidth); // Width at end (e.g., 0.1f)
Particle Types
Type 1: Sprite (Default)
Simple 2D particles. Fast and lightweight.
Definition:
{
"type": "sprite",
"texture": "yourmodid:particle/spark",
"lifetime": 30,
"scale": 0.5,
"gravity": 0.05,
"drag": 0.98,
"light": {
"mode": "glow",
"color": [1.0, 1.0, 0.0],
"intensity": 0.8
}
}
Best for: Sparks, sparkles, dust, simple effects
Type 2: Beam
Billboard between two points. Supports dynamic endpoints.
Definition:
{
"type": "beam",
"texture": "yourmodid:particle/laser",
"lifetime": 100,
"scale": 1.0,
"light": {
"mode": "glow",
"color": [1.0, 0.0, 0.0],
"intensity": 1.5
}
}
Best for: Lasers, energy blasts, directed attacks, tracers
Type 3: Geo (GeckoLib Model)
Full 3D models with animation support.
Definition:
{
"type": "geo",
"model": "yourmodid:geo/explosion.geo.json",
"texture": "yourmodid:textures/particle/explosion.png",
"animation": "yourmodid:animations/explosion.animation.json",
"anim_name": "animation.explosion.burst",
"lifetime": 50,
"scale": 0.8,
"gravity": -0.05,
"drag": 0.95,
"spin": {
"yaw": 5.0,
"pitch": 2.0,
"roll": 3.0
}
}
Properties:
model- Path to your GeckoLib.geo.jsonfiletexture- Texture for the modelanimation- Path to animation file (optional)anim_name- Which animation to play (optional)spin- Rotation speed in degrees/tick (optional)
Best for: Complex effects, explosions, magical effects, custom models
Advanced Features
Dynamic Beam Endpoints
Make beams that track targets or move over time:
import com.dracolich777.breathweapon.client.particle.SimpleBeamParticle;
// When spawning, you need to access the particle directly
// This requires a custom spawning approach or event listener
SimpleBeamParticle.EndpointProvider provider = (age, lifetime) -> {
// Calculate new position based on age
float progress = age / (float) lifetime;
// Example: Spiral endpoint
double angle = progress * Math.PI * 4; // 2 rotations
double newX = centerX + Math.cos(angle) * radius;
double newY = centerY + Math.sin(angle) * radius;
double newZ = centerZ + Math.sin(progress * Math.PI) * 5;
return new SimpleBeamParticle.BeamEndpoint(newX, newY, newZ);
};
// Set on particle (requires direct particle access via event or hook)
particle.setEndpointProvider(provider);
Physics Customization
Control how particles move:
{
"gravity": 0.1, // Fall speed (positive = down, negative = up)
"drag": 0.95, // Slow down by 5% each tick
"scale": 1.5 // Make it 1.5x bigger
}
Examples:
- Floating effect:
"gravity": -0.01, "drag": 1.0 - Slow fall:
"gravity": 0.02, "drag": 0.99 - Fading out: Use short lifetime + high drag
- Fast zoom: Use high initial velocity + low gravity
Complete Examples
Example 1: Energy Beam Attack
JSON Definition (data/mymod/particles/energy_blast.json):
{
"type": "beam",
"texture": "mymod:particle/energy_beam",
"lifetime": 80,
"scale": 0.9,
"light": {
"mode": "glow",
"color": [0.2, 0.7, 1.0],
"intensity": 1.3
}
}
Spawning Code (in an ability handler):
@SubscribeEvent
public static void onPlayerAttack(LivingAttackEvent event) {
if (event.getEntity() instanceof Player player &&
player.level() instanceof ServerLevel level) {
Vec3 from = player.getEyePosition();
Vec3 to = from.add(player.getLookAngle().scale(50));
ParticleSpawner.spawnBeam(level,
new ResourceLocation("mymod", "energy_blast"),
from, to, 0.2f);
}
}
Example 2: Explosion Effect
JSON Definition (data/mymod/particles/explosion_burst.json):
{
"type": "sprite",
"texture": "mymod:particle/explosion",
"lifetime": 40,
"scale": 1.2,
"gravity": -0.02,
"drag": 0.96,
"light": {
"mode": "pulse",
"color": [1.0, 0.6, 0.0],
"intensity": 1.5,
"pulse_min": 0.5,
"pulse_max": 1.5,
"pulse_speed": 0.15
}
}
Spawning Code:
public static void createExplosion(ServerLevel level, Vec3 center, int power) {
ParticleSpawner.burst(level,
new ResourceLocation("mymod", "explosion_burst"),
center,
power * 3, // More particles for bigger explosion
power * 0.1); // Faster spread for bigger explosion
}
Example 3: Magical Aura
JSON Definition (data/mymod/particles/mana_dust.json):
{
"type": "sprite",
"texture": "mymod:particle/mana",
"lifetime": 100,
"scale": 0.6,
"gravity": -0.005,
"drag": 0.98,
"light": {
"mode": "glow",
"color": [0.7, 0.3, 1.0],
"intensity": 0.9
}
}
Spawning Code (in entity tick):
public void createManaAura(Entity entity, ServerLevel level) {
Vec3 pos = entity.position().add(0, entity.getBbHeight() / 2, 0);
// Small burst of particles around entity
ParticleSpawner.burst(level,
new ResourceLocation("mymod", "mana_dust"),
pos,
5, // 5 particles
0.3); // Slow spread
}
Example 4: Status Effect Indicator
JSON Definition (data/mymod/particles/poison_cloud.json):
{
"type": "sprite",
"texture": "mymod:particle/poison",
"lifetime": 60,
"scale": 0.8,
"gravity": 0.01,
"drag": 0.99,
"light": {
"mode": "glow",
"color": [0.3, 0.8, 0.2],
"intensity": 0.7
}
}
Spawning Code (while effect active):
@SubscribeEvent
public static void onLivingUpdate(LivingTickEvent event) {
LivingEntity entity = event.getEntity();
if (entity.hasEffect(MobEffects.POISON) &&
entity.level() instanceof ServerLevel level &&
entity.tickCount % 5 == 0) { // Every 5 ticks
Vec3 pos = entity.position().add(
(Math.random() - 0.5) * 0.5,
Math.random() * entity.getBbHeight(),
(Math.random() - 0.5) * 0.5
);
ParticleSpawner.spawn(level,
new ResourceLocation("mymod", "poison_cloud"),
pos.x, pos.y, pos.z,
0, -0.05, 0); // Slight upward drift
}
}
Checking Particle Availability
Before spawning, verify the particle is registered:
import com.dracolich777.breathweapon.api.particle.ParticleRegistry;
ResourceLocation particleId = new ResourceLocation("yourmodid", "my_particle");
if (ParticleRegistry.has(particleId)) {
// Safe to spawn
ParticleSpawner.spawn(level, particleId, x, y, z, 0, 0, 0);
} else {
LOGGER.warn("Particle not found: {}", particleId);
}
Get particle info:
var definition = ParticleRegistry.get("yourmodid:my_particle");
if (definition != null) {
int lifetime = definition.getLifetime();
float scale = definition.getScale();
String type = definition.getType(); // "beam", "geo", "sprite"
}
Best Practices
1. Organize Your Particles
data/yourmodid/particles/
├── abilities/
│ ├── fireball.json
│ ├── lightning.json
│ └── healing_circle.json
├── effects/
│ ├── hit_effect.json
│ ├── death_burst.json
│ └── buff_indicator.json
└── ambient/
├── magical_dust.json
├── fire_embers.json
└── water_splash.json
2. Keep Lifetimes Short
Don't make particles last longer than needed:
- Sparks: 20-60 ticks
- Beams: 50-150 ticks
- Explosions: 30-100 ticks
- Ambient: 60-200 ticks
3. Test Physics
{
"gravity": 0.0, // Start with no gravity
"drag": 1.0 // Start with no drag
}
Then adjust based on visual appearance.
4. Use Appropriate Types
- Sprite - Most effects (fast)
- Beam - Targeted effects, attacks
- Geo - Special occasions (slower)
5. Reuse Definitions
Create one definition and spawn it multiple times:
for (int i = 0; i < 10; i++) {
ParticleSpawner.spawn(level,
new ResourceLocation("mymod", "explosion"),
x, y, z,
randomVelX, randomVelY, randomVelZ);
}
6. Add Descriptions to JSONs
{
"_comment": "Blue energy beam - used for mage spell attacks",
"type": "beam",
...
}
Troubleshooting
Particles Not Showing
Problem: I spawned a particle but it doesn't appear in-game.
Solutions:
- Check particle definition is in correct folder:
data/yourmodid/particles/name.json - Verify the particle ID matches the JSON filename
- Use
/reloadto reload data - Check texture path exists:
assets/yourmodid/textures/particle/name.png - Verify you're on the correct level (ServerLevel, not ClientLevel)
Debug code:
var def = ParticleRegistry.get("yourmodid:my_particle");
if (def == null) {
LOGGER.error("Particle not found in registry!");
} else {
LOGGER.info("Particle found: {}", def);
}
Texture Not Loading
Problem: Particle appears but texture is wrong.
Solutions:
- Verify texture path uses forward slashes:
"mymod:particle/name" - Check texture file is PNG format
- Texture path should NOT include
.pngextension - Texture should be in
assets/yourmodid/textures/
Beam Position Wrong
Problem: Beam starts/ends at wrong position.
Solutions:
- Use world space coordinates (not relative)
- For entities:
entity.getEyePosition()for eye level - For blocks:
new Vec3(x + 0.5, y + 0.5, z + 0.5)for center - Verify
fromandtoare not the same position
Particle Too Slow/Fast
Problem: Particle movement doesn't match expected velocity.
Solutions:
- Increase
speedparameter inburst()orstream() - Adjust
drag- lower values = keeps velocity longer - Check particle spawn velocity is correct
- Use
gravityto add momentum over time
GeckoLib Model Not Loading
Problem: Type is "geo" but model doesn't render.
Solutions:
- Verify model file path is correct
- Check
.geo.jsonfile exists - Model path should NOT include
.geo.jsonin JSON (justpathwithout extension) - Texture path must match model's texture reference
- Animation name must match exactly (case-sensitive)
API Reference
ParticleSpawner Methods
// Single particle
ParticleSpawner.spawn(level, id, x, y, z, vx, vy, vz);
ParticleSpawner.spawn(level, id, x, y, z, vx, vy, vz, extraData);
// Patterns
ParticleSpawner.burst(level, id, center, count, speed);
ParticleSpawner.stream(level, id, entity, count, speed, spread);
// Beams
ParticleSpawner.spawnBeam(level, id, from, to, width);
ParticleSpawner.spawnBeam(level, id, from, to, startWidth, endWidth);
ParticleRegistry Methods
ParticleRegistry.register(id, definition);
ParticleRegistry.get(id);
ParticleRegistry.has(id);
ParticleRegistry.getAll();
ParticleRegistry.getAllFromMod(namespace);
Need Help?
- DM me on discord or join my discord server:
- Discord Server
.themrsuit
Good luck creating amazing particles!


