Skip to content
Omar Mohamed edited this page Aug 8, 2026 · 2 revisions
VynAPI logo

VynAPI

The Vyn scripting language, embedded in Minecraft.

Version Minecraft Loaders Java

Write .vyn scripts inside resource packs and have them run in-game, reacting to what happens around the player β€” no Java required.


Table of Contents


⚑ What is VynAPI?

VynAPI is a client-side Minecraft mod that ships the Vyn scripting engine inside the game and exposes Minecraft to it.

It turns every resource pack into a potential "script pack":

your_resource_pack/
└── assets/
    └── yourmod/
        └── scripts/
            └── hello.vyn     ← plain-text Vyn script, loaded by VynAPI

Scripts are plain text files, hot-reloadable (F3 + T), and get a full view of the client world: the player, the world, blocks, sounds, biomes, game state, and more. They can draw text on the HUD, play sounds, send the player messages, react to arm swings, and respond to every sound that plays.

Because it is a mod (not a datapack), VynAPI works in singleplayer and multiplayer alike. Builds exist for Fabric, NeoForge, and Forge β€” Forge is supported on the 1.21.x builds, while the 26.x builds ship for Fabric and NeoForge.


πŸ“œ What is Vyn?

Vyn is a statement-driven scripting language designed for readability, with first-class Blueprints (an OOP system) and concurrency support. It's a standalone Java project built by Abdelaziz_Mohamed β€” think "a friendlier, modern scripting language" rather than a Minecraft-specific thing. VynAPI is the Minecraft port of that language.

A small taste of the language:

~ variables & constants
make name "Steve"
lock MAX_HEALTH 20

~ conditionals
check name == "Steve" do
    say "Hi, Steve!"
otherwise
    say "Who are you?"
end

~ loops
cycle i from 1 to 5 do
    say "Iteration: " + i
end

~ functions ("tasks")
task double takes x do
    reply x * 2
end

~ object orientation ("blueprints")
blueprint Pet do
    make name

    build takes name do
        make me.name name
    end

    task speak do
        say me.name + " says hi!"
    end
end

make dog new Pet("Rex")
dog.speak()

Vyn also has native concurrency (split do ... end), HTTP (fetch), JSON (pack/unpack), exception handling (attempt/recover), and hot-loadable modules (use).

Full language documentation lives in the Vyn repository. VynAPI embeds the same engine (me.abdelaziz:Vyn-Script) and extends it with Minecraft-native bindings.


🎯 What can you do with it?

For resource pack developers

  • No Java, no modding knowledge β€” write scripts in any text editor, drop them in your pack, and they run.
  • React to game events β€” every tick (onTick), arm swings (onSwingHand), any sound that plays (onPlaySound).
  • Read the game state β€” player health/hunger/position/velocity, the block being looked at, nearby blocks, the biome, time of day, day count, grass/foliage/water colors...
  • Do things β€” show debug text on the HUD, send the player chat messages, play sounds at positions, trigger delayed logic with wait.
  • Ship libraries β€” split code into importable scripts and reuse them with importScript / excludeScript.
  • Hot reload β€” press F3 + T while developing; your scripts reload instantly.

For mod developers

  • Extend the language β€” use the VynAddon API to register custom events and bind custom native functions, types, and constants into every script environment.
  • Reuse the pipeline β€” script discovery, parsing, event dispatch, and per-tick scheduling are all handled for you.
  • One codebase, three loaders β€” the mod is built for Fabric, NeoForge, and Forge from shared common sources (Forge on the 1.21.x builds; 26.x builds target Fabric & NeoForge).

πŸ“¦ Requirements

VynAPI supports Minecraft 1.21.5, 1.21.11, 26.1.x, and 26.2. Each Minecraft version has its own branch and build of the mod β€” download the one that matches your game version.

Minecraft Mod version Java Fabric Forge NeoForge
1.21.5 1.21.5-1.0.0 21 βœ… Loader β‰₯ 0.16.14 + Fabric API 0.128.2+ βœ… β‰₯ 55.1.0 βœ… β‰₯ 21.5.96
1.21.11 1.21.11-1.0.0 21 βœ… Loader β‰₯ 0.17.3 + Fabric API 0.141.6+ βœ… β‰₯ 61.1.0 βœ… β‰₯ 21.11.45
26.1.x 26.1-1.0.0 25 βœ… Loader β‰₯ 0.19.3 + Fabric API 0.145.1+ ❌ βœ… β‰₯ 26.1.2.94
26.2 26.2-1.0.0 25 βœ… Loader β‰₯ 0.19.3 + Fabric API 0.156.0+ ❌ βœ… β‰₯ 26.2.0.53

Note: Minecraft 26.1+ requires Java 25 (and so does the 26.x mod build). The 1.21.x builds use Java 21.

The mod is client-side only β€” install it in your client's mods folder. No server-side installation is needed (and scripts never run on the server).

πŸš€ Installation

  1. Download the VynAPI jar matching your Minecraft version and loader (e.g. 26.1-1.0.0 for NeoForge on Minecraft 26.1) from the releases page (or build it yourself β€” see Building from source).
  2. Drop the jar into your Minecraft mods folder.
  3. Launch the game.
  4. Put any resource pack containing .vyn scripts in the resource pack folder and enable it β€” scripts load automatically.

βš™οΈ How it works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Minecraft (client)                          β”‚
β”‚                                                                     β”‚
β”‚  Resource packs ──► ScriptLoader (resource reload listener)         β”‚
β”‚   assets/*/scripts/*.vyn         β”‚                                   β”‚
β”‚                                  β–Ό                                  β”‚
β”‚                        ScriptHandler (Vyn engine)                   β”‚
β”‚                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚   Mixins ──► events:   β”‚  Vyn environment           β”‚               β”‚
β”‚   onTick               β”‚  β€’ player, world, key,     β”‚               β”‚
β”‚   onSwingHand          β”‚    modLoader constants     β”‚               β”‚
β”‚   onPlaySound          β”‚  β€’ Block/Position/Sound    β”‚               β”‚
β”‚                        β”‚  β€’ debugText/importScript/ β”‚               β”‚
β”‚                        β”‚    excludeScript           β”‚               β”‚
β”‚                        β”‚  β€’ wait statement          β”‚               β”‚
β”‚                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚                                  β”‚                                   β”‚
β”‚                                  β–Ό                                   β”‚
β”‚                   BackgroundLoopHandler (tick scheduler)             β”‚
β”‚                   DebugTextHandler / GuiMixin (HUD text)             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. Discovery β€” on every resource-pack reload, ScriptLoader scans every pack in the stack for assets/<any-namespace>/scripts/*.vyn.
  2. Parsing β€” ScriptHandler feeds each file to the embedded Vyn engine (VynMain). Every event a script defines (task onTick ... end) is registered; library scripts without events are marked importable.
  3. Events β€” Mixins into the vanilla client translate game activity into script events:
    • MinecraftClientMixin β€” fires onTick every frame and drives the tick scheduler.
    • ArmSwingMixin β€” detects arm swings β†’ onSwingHand.
    • SoundListenerMixin β€” intercepts every sound played β†’ onPlaySound (with a Sound value).
    • ClientPacketListenerMixin β€” keeps the player binding fresh on login.
    • GuiMixin β€” renders debugText output on the HUD.
  4. Execution β€” scripts run on the client thread through the Vyn runtime, with Minecraft types exposed as native bindings. Delays (wait) and cooldowns are handled by BackgroundLoopHandler, a non-blocking per-tick scheduler.

πŸ§ͺ Quick start: your first script

Create a resource pack with this layout:

my_script_pack/
β”œβ”€β”€ pack.mcmeta
└── assets/
    └── example/
        └── scripts/
            └── hello.vyn

hello.vyn:

task onTick do
    debugText("Health: " + player.getHealth())
    debugText("Looking at: " + player.getTargetBlock().getName())
end

Load the pack, press F3 + T, and two lines appear in the top-left corner of your screen, updating every tick.

A few more examples

React to sounds (the sound argument is a Sound value):

task onPlaySound takes sound do
    check sound.getName() == "minecraft:block.note_block.pling" do
        player.sendMessage("A note block played near " + sound.getPosition())
    end
end

Play a sound when you swing your arm:

task onSwingHand do
    player.playSound("minecraft:entity.experience_orb.pickup", 1.0, 1.0)
end

Delayed logic with wait (20 ticks = 1 second, non-blocking):

task onTick do
    check player.getHealth() < 6 do
        wait 40 do
            player.sendMessage("Careful β€” you're low on health!")
        end
    end
end

Explore the world:

task onTick do
    make pos player.getPosition()
    make biome player.getWorld().getBiomeAt(pos)

    check player.getWorld().getDayTime() > 13000 do
        debugText("It's night in the " + biome + " β€” hello darkness!")
    end
end

πŸ“‘ Events

Scripts react to events by defining a task whose name matches the event. The mod ships with three built-in events:

onTick

Fired every client tick while in-game (not while paused, and only when the player and world exist).

task onTick do
    ~ runs 20 times per second
end

onSwingHand

Fired when the local player swings their arm (attack / use). Throttled by a short swing cooldown that is shortened by Haste and Conduit Power effects.

task onSwingHand do
    player.sendMessage("Swing! (health: " + player.getHealth() + ")")
end

onPlaySound

Fired whenever a sound plays in the client's world (ambient sounds excluded). Receives one argument: a Sound value.

task onPlaySound takes sound do
    player.sendMessage(sound.getName() + " @ vol " + sound.getVolume() + " pitch " + sound.getPitch())
end
Sound method Returns
sound.getName() Sound id, e.g. minecraft:block.note_block.pling
sound.getVolume() Volume (double)
sound.getPitch() Pitch (double)
sound.getPosition() Position where the sound plays (block coords)

Mod developers: you can register your own events β€” see VynAddon.


πŸ“‚ How scripts are loaded

  • Location β€” assets/<any-namespace>/scripts/*.vyn. The namespace doesn't matter; every pack in the stack is scanned.
  • File name = script name β€” hello.vyn becomes script hello.
  • No shadowing β€” if two packs ship a script with the same name, both are loaded (each registered under its own pack id), so packs can't silently override each other.
  • Pack id β€” every script is tagged with the id of the pack that provided it (the id shown in the resource-pack screen). Pack ids are used by importScript / excludeScript.
  • Reload β€” scripts (re)load on any resource-pack reload: F3 + T, or re-applying packs in the pack screen. A summary is printed to the log.

Script states

State Meaning
LOADED Defines at least one event task β€” registered for event dispatch.
IMPORTABLE Defines no event tasks β€” a library that other scripts can importScript.
ERROR Failed to parse or threw during execution β€” the error is shown in chat and logged.
EXCLUDED Disabled at runtime via excludeScript.

importScript(packId, scriptName)

Loads the code of an importable script into the current script's environment (a use-style module system). Typically called at the top of a script:

~ library.vyn (no event tasks)
task greet takes name do
    player.sendMessage("Hello, " + name + "!")
end
~ main.vyn
importScript("file/MyPack.zip", "library")

task onTick do
    greet("Steve")
end

If a script imports something that isn't importable (yet), loading is delayed and retried after the other scripts have loaded β€” so import order between files doesn't matter.

excludeScript(packId, scriptName)

Disables another script at runtime β€” it stops receiving events.

task onTick do
    excludeScript("file/OtherPack.zip", "annoying_beep")
end

🧰 Native API reference

Everything below is available inside every .vyn script.

Constants

Constant Type Description
player Player The local player (and their ridden entity).
world World The client world.
key Key Translation-key helpers.
modLoader ModLoader Mod-loader / environment info.

Constructible types

make pos new Position(100, 64, -200)
make snd new Sound("minecraft:block.note_block.pling", 1.0, 1.0, pos)
Type Constructor Notable methods
Position new Position(x, y, z) getX/getY/getZ, setX/setY/setZ, getDistanceTo(other | x, y, z)
Sound new Sound(id, volume, pitch, position) getName, getVolume, getPitch, getPosition, plus setters

player β€” the local player

Identity & state

Method Returns
player.getName() Player name
player.getUUID() UUID string
player.getGameMode() Game mode name (survival, creative, ...)
player.isLocalPlayer() / isMainPlayer() Whether this is the local player
player.isSpectator() Spectator?
player.getPermissionLevel() Permission level
player.isCamera() Is the camera entity

Position & rotation

Method Returns
player.getPosition() Position (block coords)
player.getX() / getY() / getZ() Exact coordinates
player.getEyePosX() / getEyePosY() / getEyePosZ() Eye position
player.getYaw() / getPitch() Rotation
player.getHeadYaw() / getBodyYaw() Head / body yaw
player.getWidth() / getHeight() Entity dimensions
player.getFallDistance() Fall distance
player.getVelocityX() / getVelocityY() / getVelocityZ() Current velocity
player.getSpeed() Movement speed

Vitals

Method Returns
player.getHealth() Health
player.getMaxHealth() Max health
player.getAbsorptionAmount() / getMaxAbsorption() Absorption
player.getFoodLevel() / getSaturationLevel() Hunger / saturation
player.getAirSupply() / getMaxAirSupply() Air
player.getArmor() / getArmorCoverPercentage() Armor
player.isAlive() / isDead() Life state
player.getLuck() Luck
player.getHurtTime() / getDeathTime() Hurt / death timers
player.getStuckArrowCount() / getStingerCount() Arrows / stingers
player.getMoodPercentage() Mood (muffled music)

Blocks & world

Method Returns
player.getTargetBlock() Block being looked at
player.getSteppingBlock() Block the player is standing on
player.getNearbyBlocks(radius) List of non-air Blocks in a cube around the player
player.getWorld() World object

Actions

Method Effect
player.sendMessage(text) Shows a chat-style message to the local player
player.playSound(id, volume, pitch) Plays a sound to the player
player.playSound(sound) Plays a Sound value
player.playSoundWorld(position, id, volume, pitch) Plays a sound at a world position
player.playSoundWorld(position, sound) Same, with a Sound value

world β€” the client world

Method Returns
world.getBlock(x, y, z) Block at coordinates
world.getDimension() Dimension id (e.g. minecraft:overworld)
world.getDayTime() / getGameTime() Time of day / total game time (ticks)
world.isBrightOutside() Is it bright outside?
world.getBiomeAt(position) Biome id (e.g. minecraft:plains)
world.getGrassColor(position) / getFoliageColor(position) / getWaterColor(position) Biome-tinted colors (int)
world.calculateDistanceBetweenPositions(pos1, pos2) Distance between two positions

Block

Method Returns
block.getName() Item id of the block (e.g. minecraft:stone)
block.getPosition() Position of the block
block.isAir() Is air?
block.hasBlockTag(tagId) Has a block tag (e.g. minecraft:logs)?
block.getLightBlock() / getLightEmission() Light values
block.getSkyDarken() Sky darkening
block.canBeReplaced() Replaceable?
block.hasBlockEntity() Has a block entity?
block.requiresCorrectToolForDrops() Needs the right tool?
block.ignitedByLava() / isRandomlyTicking() / isSolidRender() / canOcclude() / hasLargeCollisionShape() Block-behavior flags
block.instrument() Note-block instrument sound id

key β€” translations

Method Returns
key.getTranslatedKey("key.attack") The translated, human-readable name for a key

modLoader β€” environment info

Method Returns
modLoader.isModLoaded("fabric-api") Is a mod loaded?
modLoader.isResourcePackLoaded("My Pack") Is a resource pack selected?
modLoader.getRawGameVersion() Raw game version string

Functions

Function Description
debugText(text) Draws text at the top-left of the HUD for one tick (call every tick to keep it visible).
importScript(packId, scriptName) Loads an importable script's code into the current script.
excludeScript(packId, scriptName) Disables a loaded script (it stops receiving events).

wait statement

VynAPI adds a Minecraft-specific statement to the Vyn parser:

wait 20 do
    ~ runs once, 20 ticks (1 second) later, without blocking the game
    player.sendMessage("Time's up!")
end

wait is powered by BackgroundLoopHandler, a tick-based scheduler that runs callbacks on the client thread.

Note: the wait duration is used as the internal slot id, so a second wait with the same duration that starts while the first is still pending is skipped (only one pending wait per duration at a time).


🧭 Vyn language quick tour

Full docs: github.com/Abdelaziz1586/Vyn

Concept Syntax
Variable make x 10
Constant lock MAX 100
If / else check cond do ... otherwise ... end
Range loop cycle i from 1 to 10 do ... end
While loop cycle while cond do ... end
Break escape
Function task name takes a, b do ... reply value end
Class blueprint Name do ... end (with build, demolish, task methods, me self, mimics inheritance)
Concurrency split do ... end
Sleep (blocking) hold 2000 (milliseconds)
Exceptions attempt do ... recover err do ... end
HTTP / JSON fetch(url), pack(obj), unpack(json)
Collections new List, new Map, size(obj), at(list, i), sort(list)
Print say "text" (console)

In Minecraft, never block the client thread. Use VynAPI's wait instead of hold for delays, and keep per-tick work light (it runs 20Γ—/second on the render thread's tick).


🧩 For mod developers: VynAddon

VynAddon is the extension point for mods. Subclass it to:

  1. Register custom events (passed to the super constructor) β€” they become callable from scripts as task <eventName> ... end.
  2. Bind native functions, types, and constants into every script environment (via onEnable()).
public final class MyAddon extends VynAddon {

    public MyAddon() {
        super("onMyEvent"); // custom event scripts can listen to
    }

    @Override
    public Consumer<Environment> onEnable() {
        return env -> {
            // expose a custom type so scripts can write `new MyType(...)`
            NativeBinder.bind(env, MyType.class);

            // expose a constant
            NativeBinder.defineConstant(env, "myModVersion", "1.0.0");

            // expose a native function
            env.defineFunction("myFunction", new NativeFunction((e, args) -> {
                // ... your logic ...
                return null;
            }));
        };
    }
}

Then fire your event from anywhere in your mod:

ScriptHandler.fireEvent("onMyEvent", someValue, anotherValue);

Any loaded script defining task onMyEvent takes a, b do ... end will receive it.

Useful ScriptHandler utilities:

Method Purpose
ScriptHandler.fireEvent(name, args...) Fire an event to all loaded scripts
ScriptHandler.getEventNames() / containsEvent(name) Inspect registered events
ScriptHandler.registerSTD(addon) / unregisterSTD(addon) Register/unregister an addon
ScriptHandler.addScript(packId, name, content) Inject a script programmatically

Platform services

Cross-loader code uses Java ServiceLoader:

  • Services.PLATFORM β†’ IPlatformHelper (getPlatformName(), isModLoaded(), isDevelopmentEnvironment(), ...)
  • ModLoader instances are backed by per-loader ModLoaderHandler implementations (Fabric/Forge/NeoForge).

The Vyn engine jar (me.abdelaziz:Vyn-Script) is bundled into the mod automatically β€” on Fabric via include, on Forge/NeoForge via Jar-in-Jar.


πŸ”¨ Building from source

Requirements: JDK 21 (for the 1.21.x branches) or JDK 25 (for the 26.x branches), and the Vyn engine artifact available to Gradle.

The Vyn engine (me.abdelaziz:Vyn-Script:1.0-SNAPSHOT) is resolved from mavenLocal() β€” if you haven't published it, install it first:

git clone https://github.com/Abdelaziz1586/Vyn.git
cd Vyn
mvn clean install

Then build VynAPI. Each supported Minecraft version lives on its own branch β€” check out the one you want before building:

# e.g. the 1.21.11 branch (also: 1.21.5, 26.1, 26.2)
git checkout 1.21.11

# everything
./gradlew build

# or per loader
./gradlew :fabric:build
./gradlew :neoforge:build
# Forge is available on the 1.21.x branches only:
./gradlew :forge:build

Run a dev client:

./gradlew :fabric:runClient
./gradlew :neoforge:runClient
./gradlew :forge:runClient   # 1.21.x branches only

Built jars land in <loader>/build/libs/ (e.g. fabric/build/libs/).


πŸ—‚ Project structure

VynAPI/
β”œβ”€β”€ common/          # Shared code: engine integration, mixins, models, handlers
β”‚   └── src/main/java/studio/meraki/vynapi/
β”‚       β”œβ”€β”€ handler/
β”‚       β”‚   β”œβ”€β”€ client/      # BackgroundLoopHandler (tick scheduler), InteractionHandler
β”‚       β”‚   β”œβ”€β”€ other/       # DebugTextHandler, ModLoader handlers
β”‚       β”‚   └── script/      # ScriptHandler (core), ScriptLoader (pack scanning)
β”‚       β”œβ”€β”€ mixin/           # client/player/render/sound mixins (events & HUD)
β”‚       β”œβ”€β”€ model/           # VynAddon, Script/PackScripts, native bindings
β”‚       β”‚   β”œβ”€β”€ function/    # debugText, importScript, excludeScript
β”‚       β”‚   β”œβ”€β”€ statement/   # wait statement
β”‚       β”‚   └── variable/    # Player, World, Block, Position, Sound, Key, ModLoader
β”‚       └── platform/        # ServiceLoader plumbing (IPlatformHelper)
β”œβ”€β”€ fabric/          # Fabric entrypoints + loader handler + reload listener
β”œβ”€β”€ forge/           # Forge entrypoint + loader handler (Jar-in-Jar) β€” 1.21.x branches only
β”œβ”€β”€ neoforge/        # NeoForge entrypoint + loader handler (Jar-in-Jar)
β”œβ”€β”€ buildSrc/        # MultiLoader Gradle convention plugins
└── gradle.properties

πŸ“„ License & credits

  • License: All rights reserved β€” Β© 2026 Abdelaziz, Omar. See LICENSE.
  • Vyn language: github.com/Abdelaziz1586/Vyn β€” MIT licensed.
  • Authors: OmarDotContent & Abdelaziz_Mohamed β€” Vyn Port by Abdelaziz_Mohamed, Mod Port by OmarDotContent.