Skip to content

Getting Started

Stasis, the Shattered edited this page Jun 21, 2026 · 7 revisions

Getting Started with VoxelFX

1. Add the dependency

VoxelFX is published via JitPack. Add the repository and dependency to your build.gradle:

repositories {
    maven { url = 'https://jitpack.io' }
}

dependencies {
    implementation 'com.github.StainlessStasis:VoxelFX:MOD_VERSION-MINECRAFT_VERSION'
}

Replace the placeholders with their respective values. E.g. 1.0.0-26.1.2.

2. Building an animation

Call VfxAnimationBuilder.create() to get a new builder, then chain methods to set properties of the animation. For example, to create an infinitely looping, spinning block:

VfxAnimation animation = VfxAnimationBuilder.create()
  .blockState(Blocks.DIAMOND_BLOCK.defaultBlockState(), builder -> {})
  .scale(0.5f, builder -> {}) // constantly half scale - no additional keyframes
  .translation(builder -> builder
          .addKeyframe(0.5f, 0, 4, 0, EasingType.LINEAR) // move up 4 blocks
          .addKeyframe(1f, 0, 0, 0, EasingType.LINEAR)) // move back down
  .loopInfinite()
  .build(80); // animation length defined in ticks

For more examples, see VfxDemos and NovaBombDemo.

BlockState & ItemStack channels

blockState(...) and itemStack(...) behave differently from the other channels. Translation, scale, rotation, and overlay all interpolate smoothly between keyframes, but block states and item stacks can't blend into each other, so these channels are discrete. The value snaps to the most recent keyframe whose time has passed, with no blending in between.

.blockState(Blocks.DIAMOND_BLOCK.defaultBlockState(), builder -> builder
        .addKeyframe(0.3f, Blocks.GOLD_BLOCK.defaultBlockState())
        .addKeyframe(0.6f, Blocks.IRON_BLOCK.defaultBlockState()))

This holds DIAMOND_BLOCK until 30% progress, then instantly swaps to a gold block, and finally an iron block at 60%. There's no fading between block types, so this is commonly combined with overlay color/intensity keyframes timed to match. A color transition can give the illusion of a smooth swap (see the Overlay section in Advanced Usage).

Both blockState and itemStack can be set on the same animation if you want a block and item rendered together (e.g. an item floating inside a glass casing), but you may find it easier to just leave them separate, so that they can be scaled and such individually.

3. Playing animations

Animations are played from a VfxEntity, which is obtained via VfxEntity.create(level, position). Position is set in the constructor and you don't need to call anything else before playing an animation.

Do not add the entity to the level! VFX entities are automatically added to a cache (VfxEntityCache) and ticked/rendered there. Adding the entity to the level can break things or even cause crashes.

To play an animation, simply call playAnimation(animation). You can also use playAnimation(animation, progressOffset) to start it mid-animation, where the offset is from 0-1:

// start the animation already 40% of the way through
vfxEntity.playAnimation(animation, 0.4f);

There is also playAnimationWithOffset, which accepts either seconds (float) or ticks (int) instead of a normalized progress value. Useful when you want to express the offset in real time rather than as a percentage:

vfxEntity.playAnimationWithOffset(animation, 1.5f); // start 1.5 seconds in
vfxEntity.playAnimationWithOffset(animation, 30); // start 30 ticks in

These methods will immediately start the animation, overriding the currently playing one. To queue animations so that they play one after another, use playOrQueueAnimation(animation). Play two animations in sequence like so:

VfxAnimation animation1 = VfxAnimationBuilder.create()
                .blockState(Blocks.DIAMOND_BLOCK.defaultBlockState(), builder -> {})
                .scale(0.5f, builder -> {})
                .translation(builder -> builder
                        .addKeyframe(0.5f, 0, 4, 0,  EasingType.LINEAR)
                        .addKeyframe(1f, 0, 0, 0,  EasingType.LINEAR))
                // removed the infinite loop here
                .build(80);
VfxAnimation animation2 = VfxAnimationBuilder.create()
                .blockState(Blocks.NETHERITE_BLOCK.defaultBlockState(), builder -> {})
                .scale(0.5f, builder -> builder
                        .addKeyframe(0.5f, 1f, EasingType.LINEAR)) // scale to 1x (normal) size half way through
                .translation(builder -> builder
                        .addKeyframe(0.5f, 0, 4, 0, EasingType.LINEAR)
                        .addKeyframe(1f, 0, 0, 0, EasingType.LINEAR))
                .build(80);
        VfxEntity vfxEntity = VfxEntity.create(level, pos);
        vfxEntity.playOrQueueAnimation(animation1);
        vfxEntity.playOrQueueAnimation(animation2);

Where to go next

Once you're comfortable building and playing single animations, head to Advanced Usage for more detailed information about each feature..

If you'd rather test things in game first without writing code, test out some of the demo effects with /voxelfx demo.