Skip to main content

MoLang

info

If you are unfamiliar with what MoLang is and want to use it to create an animation, refer to the MoLang page in the Emotecraft wiki.

PAL evaluates MoLang with mochafloats, a fork of Mocha that computes with floats instead of doubles. Almost all of Bedrock MoLang's functionality is present, and all MoLang queries and functions have been implemented — here is the full documentation for Bedrock MoLang.

MoLang lets you compute a keyframe value while the animation plays instead of baking it into keyframes, so you can react to the player's state or build motion that never repeats itself.

Every animation controller creates its own MoLang engine, so variable/v values live as long as the controller and are shared between all animations it plays, but never leak into another player's controller.

Where you can use MoLang

MoLang expressions are written as strings inside the keyframe vectors of Blockbench/Bedrock format animations:

{
"format_version": "1.8.0",
"animations": {
"molang_test": {
"animation_length": 5,
"bones": {
"body": {
"rotation": {
"vector": [0, "math.sin(q.anim_time * 50) * 45", 0]
}
},
"right_arm": {
"position": {
"vector": [0, "q.is_on_fire ? 5 : 0", 0]
}
}
}
}
}
}

Each of the three components of a vector can be either a number or a MoLang string, and it is re-evaluated every frame.

warning

The PlayerAnimator/Emotecraft JSON format (the one with an emote object instead of animations) reads keyframe values as plain numbers — MoLang is not supported there.

note

Rotation and bend values are written in degrees, exactly like plain numeric keyframes — PAL converts the result of the expression to radians for you. Positions and scales are used as-is.

Syntax

Everything in MoLang evaluates to a number; booleans are just 1 and 0.

ScopeAliasDescription
queryqRead-only values provided by PAL and by mods. See Built-in queries.
variablevYour own values, kept for the lifetime of the animation controller.
temptYour own values, discarded after the expression finishes.
mathMath functions and constants. See Math functions.

Names are case-insensitive, and both the full name and the alias always point at the same thing.

Supported operators: + - * /, ! && ||, < <= > >= == !=, = (assignment), ? : (ternary), ?? (null coalescing), -> (arrow), . (member access), [] (array access), (), {}, ,, ;. The keywords true, false, return, break and continue are supported, as well as the loop(count, expression) and for_each(variable, array, expression) functions.

note

There is no % operator — use math.mod(a, b) instead.

Multi-statement expressions have to end with a return:

"vector": [0, "v.angle = q.anim_time * 20; return math.sin(v.angle) * 45;", 0]

Built-in queries

These two are available on every controller, even outside Minecraft:

QueryDescription
anim_timeTime in seconds since the current animation started.
controller_speedThe current animation speed of the controller.

The rest are registered by PAL itself and read from the avatar — the player (or mannequin) the controller is animating. Every query returning a yes/no answer evaluates to 1 or 0.

Time and world

QueryDescription
dayThe world's game time in days (game time / 24000).
time_of_dayThe world's clock time in days, so 0.0 is the start of a day and 1.0 is the end of it.
time_stampThe world's game time in ticks.
moon_phaseThe index of the current moon phase.
moon_brightnessThe star/moon brightness at the avatar's position.
frame_alphaThe partial tick of the frame being rendered (0.01.0).
life_timeLike anim_time, but 0 while the controller is not active.
actor_countHow many entities the client is currently rendering.
distance_from_cameraDistance in blocks between the camera and the avatar.
is_first_personWhether the avatar is the local player and the camera is in first person.

Position and movement

QueryDescription
body_x_rotationThe avatar's view pitch, interpolated with the partial tick.
body_y_rotationThe avatar's body yaw, interpolated with the partial tick.
head_x_rotationThe avatar's head pitch, interpolated with the partial tick.
head_y_rotationThe avatar's head yaw, interpolated with the partial tick.
yaw_speedHow much the yaw changed since the last tick.
ground_speedHorizontal speed in blocks per tick.
vertical_speedVertical (Y) speed in blocks per tick.
is_movingWhether the animation data considers the avatar to be moving.
movement_directionThe direction the avatar is moving in as a direction id, or 6 when it isn't moving.
cardinal_facingThe direction the avatar is facing as a direction id.
cardinal_facing_2dSame as cardinal_facing, but returns 6 for up and down.
cardinal_player_facingThe direction the avatar is facing as the ordinal of the direction.
limb_swingThe position of the vanilla walk animation.
limb_swing_amountThe speed of the vanilla walk animation at the current partial tick.
is_on_groundWhether the avatar is standing on the ground.
is_sneakingWhether the avatar is crouching.
is_sprintingWhether the avatar is sprinting.
is_swimmingWhether the avatar is swimming.
is_wall_climbingWhether the avatar is on a climbable block.
sleep_rotationThe Y rotation of the bed the avatar is sleeping in, or 0.
is_sleepingWhether the avatar is sleeping.

Direction ids are the vanilla ones: 0 down, 1 up, 2 north, 3 south, 4 west, 5 east.

State

QueryDescription
healthThe avatar's current health.
max_healthThe avatar's maximum health.
hurt_timeTicks left of the hurt animation, or 0.
invulnerable_ticksTicks left of invulnerability, or 0.
death_ticksTicks since the death animation started, or 0.
is_aliveWhether the avatar is alive.
is_babyWhether the avatar is a baby.
is_angryWhether the avatar is an angry neutral mob.
is_breathingWhether the avatar's air supply is full.
is_on_fireWhether the avatar is on fire.
is_fire_immuneWhether the avatar's entity type is immune to fire.
is_invisibleWhether the avatar is invisible.
is_silentWhether the avatar is silent.
is_in_waterWhether the avatar is in water.
is_in_lavaWhether the avatar is in lava.
is_in_water_or_rainWhether the avatar is in water or being rained on.
is_in_contact_with_waterSame as is_in_water_or_rain.
has_collisionWhether the avatar collides with blocks.
has_gravityWhether gravity applies to the avatar.
scaleThe avatar's scale.
player_levelThe player's experience level, or 0 for non-players.
get_actor_info_idThe avatar's entity network id.

Equipment and items

QueryDescription
blockingWhether the avatar is blocking.
is_using_itemWhether the avatar is using an item.
main_hand_item_use_durationHow long the main hand item has been used, in seconds, or 0.
main_hand_item_max_durationThe full use duration of the main hand item, in ticks.
equipment_countHow many armor slots are filled.
has_head_gearWhether something is worn in the head slot.
has_capeWhether the avatar's skin has a cape.
is_saddledWhether the avatar has something in the saddle slot.

Riding

QueryDescription
is_ridingWhether the avatar is riding something.
has_riderWhether something is riding the avatar.
has_player_riderWhether a player is riding the avatar.
has_ownerWhether the avatar is an ownable entity that has an owner.
is_leashedWhether the avatar is leashed.
rider_body_x_rotationThe pitch of the first passenger's body, or 0.
rider_body_y_rotationThe yaw of the first passenger's body, or 0.
rider_head_x_rotationThe pitch of the first passenger's head, or 0.
rider_head_y_rotationThe yaw of the first passenger's head, or 0.

Math functions

All the standard MoLang math functions are available.

note

math.sin and math.cos take degrees, and math.asin, math.acos, math.atan and math.atan2 return degrees.

FunctionDescription
math.abs(value)Absolute value.
math.acos(value)Arc cosine, in degrees.
math.asin(value)Arc sine, in degrees.
math.atan(value)Arc tangent, in degrees.
math.atan2(y, x)Arc tangent of y/x, in degrees.
math.ceil(value)Rounds up.
math.clamp(value, min, max)Clamps the value between min and max.
math.copy_sign(value, sign)The magnitude of value with the sign of sign.
math.cos(degrees)Cosine of an angle in degrees.
math.sin(degrees)Sine of an angle in degrees.
math.d2r(degrees)Converts degrees to radians.
math.r2d(radians)Converts radians to degrees.
math.die_roll(num, low, high)Sum of num random rolls between low and high.
math.die_roll_integer(num, low, high)Same, but with whole numbers.
math.exp(value)e raised to the given power.
math.ln(value)Natural logarithm.
math.floor(value)Rounds down.
math.round(value)Rounds to the nearest whole number.
math.trunc(value)Drops the fractional part.
math.hermite_blend(t)3t² - 2t³, a smooth 0→1 curve.
math.inverse_lerp(start, end, value)Where value sits between start and end, as 0.01.0.
math.lerp(start, end, t)Interpolates between start and end.
math.lerprotate(start, end, t)Interpolates between two angles in degrees, taking the shortest way around.
math.min_angle(degrees)Wraps an angle into the -180180 range.
math.max(a, b)The bigger of the two values.
math.min(a, b)The smaller of the two values.
math.mod(a, b)Remainder of a / b.
math.pow(a, b)a raised to the power of b.
math.sqrt(value)Square root.
math.random(min, max)Random value between min and max.
math.random_integer(min, max)Random whole number between min and max.
math.sign(value)-1, 0 or 1 depending on the sign of the value.
math.piThe constant π.
math.eThe constant e.

Easing functions

On top of the standard functions, PAL exposes every easing type as math.ease_*(start, end, t), where t goes from 0.0 to 1.0:

"vector": [0, "math.ease_out_bounce(0, 90, q.anim_time / 2)", 0]

All of them exist in ease_in_, ease_out_ and ease_in_out_ flavours:

FamilyFunctions
Sinemath.ease_in_sine, math.ease_out_sine, math.ease_in_out_sine
Quadraticmath.ease_in_quad, math.ease_out_quad, math.ease_in_out_quad
Cubicmath.ease_in_cubic, math.ease_out_cubic, math.ease_in_out_cubic
Quarticmath.ease_in_quart, math.ease_out_quart, math.ease_in_out_quart
Quinticmath.ease_in_quint, math.ease_out_quint, math.ease_in_out_quint
Exponentialmath.ease_in_expo, math.ease_out_expo, math.ease_in_out_expo
Circularmath.ease_in_circ, math.ease_out_circ, math.ease_in_out_circ
Backmath.ease_in_back, math.ease_out_back, math.ease_in_out_back
Elasticmath.ease_in_elastic, math.ease_out_elastic, math.ease_in_out_elastic
Bouncemath.ease_in_bounce, math.ease_out_bounce, math.ease_in_out_bounce

See Easings.net for what each of them looks like.

Registering your own queries

Mods can add their own queries through the MoLang event. It fires once for every animation controller that is created, and gives you the controller, the engine and the query binding.

On Fabric and in Architectury common code:

MolangEvent.MOLANG_EVENT.register((controller, engine, queryBinding) -> {
MolangLoader.setDoubleQuery(queryBinding, "mymod_hand_distance", ctrl ->
ctrl instanceof PlayerAnimationController playerController ? getHandDistance(playerController.getAvatar()) : 0);
MolangLoader.setBoolQuery(queryBinding, "mymod_is_aiming", ctrl -> ...);
});

On NeoForge, listen for com.zigythebird.playeranim.neoforge.event.MolangEvent on the NeoForge event bus instead:

@SubscribeEvent
public static void onMolang(MolangEvent event) {
event.setDoubleQuery("mymod_hand_distance", ctrl -> ...);
event.setBoolQuery("mymod_is_aiming", ctrl -> ...);
}

The lambda you pass is evaluated every time the query is read, so it always sees the current state.

danger

Query names must not contain dots. A dot is the member access operator, so q.mymod.hand_distance is read as "the hand_distance property of the mymod property of query". query has no mymod property, so the expression silently evaluates to 0, your lambda is never called, and nothing is logged. Use underscores instead — mymod_hand_distance — and read it as q.mymod_hand_distance.

warning

The event fires for every controller of every player, including the ones registered by other mods, so check the controller before returning anything meaningful — that it is a PlayerAnimationController, and, if the query only makes sense for your own layer, that it is yours. Queries can only be added while the event is running — the binding is made immutable right after.

tip

Query names are case-insensitive, and registering a name that already exists overwrites it, so prefix your queries with your mod id to avoid breaking the built-in ones or another mod's. The setDoubleQuery/setBoolQuery methods return false when the query could not be registered.