ChatTriggers is a framework for Minecraft Forge that allows for mods to be scripted, in languages such as JavaScript. Scripts are able to be hot reloaded, which means you can make changes to your mod without restarting!

JavaScript scripts are executed with our custom fork of Mozilla's Rhino library. Our custom fork supports many ES6 and ESNEXT features. For the (almost) full feature list,check our compatibility table;


To set up ChatTriggers, all you have to do is put the ChatTriggers jar into your mods folder, and launch Minecraft. By default, ChatTriggers modules are stored in .minecraft/config/ChatTriggers/modules/, but this can be changed in the configuration. To access the ChatTriggers settings, type /ct settings in game. The rest of this tutorial will refer to this directory as the "modules directory", and will assume it is in the default location.

Creating a module

To create a module, create a folder in your modules folder. The folder can have whatever name you want, but typically it is just the name of the module. Our module will be called ExampleModule. Our folder structure now looks like .minecraft/config/ChatTriggers/modules/ExampleModule.

The metadata file

An example metadata.json file

  "name": "ExampleModule",
  "creator": "YourNameHere",
  "description": "Our first module!",
  "version": "1.0.0",
  "entry": "index.js"

All modules MUST have a metadata.json file. This file contains important information about our module. You can see an example of this file to the right. The metadata file contains a number of important fields, documented here:


We now need to create our script files. Typically, the root file of your module is named index.js. This is a general web development practice. You can name your files whatever your want, however one benefit of having an index.js file is that if someone tries to import from your module folder directly, instead of a specific file in your module, it will be imported from the index.js file, if one exists. If no index.js file exists, other people will have to import directly from the files themselves.

The Basics

Registering a Trigger

We register a WorldLoad trigger like so:

The argument passed into the register function is the function you want to trigger:

function exampleWorldLoad() {


register("worldLoad", exampleWorldLoad);

You can also register an anonymous function for simplicity:

register("worldLoad", () => {


In ChatTriggers, "triggers" are events that get fired when a certain action happens in game, like a sound being played or a chat message being sent. Let's start with one of the simplest triggers: WorldLoad. In order to register a trigger, we use the provided register function. It takes the trigger name as the first argument (case-insensitive), and the function to run as the second argument. You can see a complete list of these triggers on our javadocs, under IRegister.

Now any code inside of our exampleWorldLoad trigger will be ran whenever a world is loaded. From here, you can do many things, such as interacting with Minecraft methods or getting information from arguments that certain triggers may pass in.

The "Chat" Trigger

A basic chat trigger

register("chat", (player, message, event) => { + ": " + message);
}).setCriteria("<${player}> ${message}");

This is how you can set a chat trigger. Chat triggers trigger when a chat message matches the specific criteria that is set. In this case, the trigger will fire whenever a chat message matches a chat message <${player}> ${message}. You can reference variables by adding ${ } around the variable name. All variables must be accounted for in the function parameters, following the order they appear in. Also keep in mind that the event that is fired is always the last parameter.

You may notice that the original message is still being sent, which looks really ugly. To fix this, we can cancel the event.

register("chat", (player, message, event) => {
  cancel(event); + ": " + message);
}).setCriteria("<${player}> ${message}");

Setting criteria as it is in the example to the right will try to match the exact message. In order to allow a message to just contain the criteria, you can add .setContains() after setCriteria. An example of this is to the right. With this, hi Player! how are you?, etc. will also trigger. These are just simple examples, but the idea still is there. The message just has to contain the criteria you set when you add .setContains(). There are also setStart and setEnd modifiers you can use instead.

Also, if the criteria you set contains color codes (starting with § or &), the message will try to match the exact color throughout the message.

This will be triggered if a player says hi User! anywhere inside their message

register("chat", (player, event) => {"howdy " + player);
}).setCriteria("hi ${player}!").setContains();

When trying to pick up certain messages that aren't sent by other players, try not to use setContains as a player could type the message that's being matched and trigger your code.

MessageSent Trigger

Display "Pong!" in response to YOUR message sent containing "ping"

register("messageSent", (message, event) => {
  if (message.toLowerCase().includes("ping")) {"Pong!");

In addition to letting you know when an event has occurred by calling your function, many triggers pass through additional information about their respective event. Let's take a look at a different trigger: MessageSent. This trigger is fired whenever the client (you) sends a message.

Let's make a trigger that, whenever you sent a message with the word "ping", displays the message "Pong!". In order to do this, we need to accept the arguments passed in by the MessageSent trigger. You can see all the arguments that a trigger passes through in the javadocs linked above. The MessageSent trigger passes in the message event and the actual message.

Many triggers are cancellable, meaning that they actually fire before the event in question happens. In our case, our MessageSent trigger will fire before the chat message is actually sent. Cancelling the event is as easy as calling cancel(event), however we won't do that here.

We are interested in the message parameter. We simply check if it contains the word we are interested in, and if so, we use the ChatLib utility to send a message only visible to the player. You can read more about the ChatLib utility here

Command Trigger

Another one of the most common triggers is the Command trigger. This allows the user to make custom commands of their choosing.

register("command", (user) => {"Hi " + user);

Commands are one of the few triggers that do not have event as a parameter. This example creates a command which can be called through /mycommand. The arguments of the function are the arguments the user types into the command. If the user types /mycommand kerbybit, then the function will be triggered, and Hi kerbybit will be printed into chat.

We can also set command aliases using the setAliases function. It accepts any amount of arguments. Let's say we want /mc and /myc to also be valid commands that will trigger our code. We would just add it anto the trigger, in no specific order.

register("command", (user) => {"Hi " + user);
}).setName("mycommand").setAliases("mc", "myc");

Module Organization

ES6 style import syntax (preferred)

import playerLocation from "WhereAmi";

This imports a function from WhereAmI's index file. You could specify additional files i.e. WhereAmI/otherfile You can also import from other files within the current module using local file paths such as ./folder/file (where folder resides within WhereAmI).

This imports a function from another module, called WhereAmI, to get a players location.

When making a module, it is important to know how ChatTriggers loads your files so you can organize them efficiently. When your module is loaded, only the file specified as the entry in metadata.json is loaded. Any other code you want to run must be imported, through the ES6 style import syntax.


The ChatLib is a utility provided to imports for interacting with Minecraft chat. It has functionality to send, edit, and delete messages in Minecraft's chat. You can create clickable, and hoverable, text in chat, run commands, and much more.

Sending messages

This sends a client-side message in chat"Coming from the code!");

This first example shows how to display a basic chat message. This message differs from normal messages in that it does NOT trigger chat triggers. It is also not visible to other players.

Message objects

This example sends messages that have clickable and hoverable text

const clickableMessage = new Message(
  "This is not clickable. ",
   new TextComponent("This is clickable").setClick("run_command", "/help"),

const hoverableMessage = new TextComponent("This message does nothing when clicked.").setHoverValue("But it shows this text when hovered over!");;;

Here we are creating new Message objects. These are required to send messages that have clickable or hoverable text. The constructor of a Message can take as many Strings or TextComponents as you want, simply separate them with commas as shown in the first example.

TextComponents are nice little wrappers that allow you to customize a small chunk of a message. You can change what happens when you click or hover on the message. You can also directly send a TextComponent as seen with the hoverable message. All of these can use formatting codes with the & symbol.


The first message we create is a message that has clickable, and non-clickable, text. The first part is regular text, followed by a clickable part that runs the /help command when clicked, and shows the hoverText when the mouse is hovering over that part of the message. Then, at the end, it has a non-clickable exclamation point.


The second message created and chatted is a message that only contains a hoverable message. Nothing will be activated or ran when the message is clicked.

Message IDs

This is how you send a chat message with an ID, and then delete it

new Message("This will be deleted!").setChatLineId(5050).chat();


Every message can have an ID specified, which can then be deleted from chat. This is best suited for auto-replacing menus, chat messages you only want to display in chat for a certain amount of time, etc.

This example also showcases an alternative method of chatting a Message, as there is a simple helper method, Message#chat()

Only one chat message can have the same ID, as it will replace any messages with the same ID before sending. The ID is specified in the message object, and you pass the same ID to ChatLib.clearChat(id) to delete it.

Editing chat

This is how you edit a chat message after it has been sent to chat"Hey there! This will change...");

ChatLib.editChat("Hey there! This will change...", "And... changed!")

ChatLib.editChat(message, replacer) is a simple method that takes in an unformatted message and replaces all instances of it with the replacer. This is a slightly laggy operation if done extremely rapidly (i.e. around 60 times per second). The editChat method can also take the Message ID as the first argument.

Specially formatted messages

This is how you center a chat message"This is in the center of the chat!"));

This is how you make a line break"-"));

Centered messages

To center a message in chat (padded by spaces), use the ChatLib.getCenteredText(text) method, and then chat what's returned.

Line breaks

To create a line break in chat that will be the exact length of the chat box no matter the user's width and size settings, use the ChatLib.getChatBreak(seperator) method. This can take any seperator, like "-" as we used in the example.

Chat message from event

This is how you get the unformatted message from a chat event

function onChatReceived(event) {
  const unformattedMessage = ChatLib.getChatMessage(event);

This is how you get the formatted message from a chat event

function onChatReceived(event) {
  const formattedMessage = ChatLib.getChatMessage(event, true);

Unformatted chat

To get the unformatted chat from a chat event passed into a function by a Chat Trigger, pass it to the ChatLib.getChatMessage(event) method, which returns an unformatted string.

Formatted chat

However, if you want the formatted version of the chat message, append the true flag to the ChatLib.getChatMessage(event, formatted) method.


Rendering is where modules can draw most anything on to the game screen. All 2D rendering involves calling methods in the Renderer object. 3D rendering involves calling methods in the Tessellator object.

Setting up

Function to be ran everytime the game overlay is rendered

register("renderOverlay", myRenderOverlay);

function myRenderOverlay() {


Rendering has to be done every frame of the game, otherwise it will only be on the screen for one frame. There are many different render triggers, and they all start with Render. The most common render trigger is RenderOverlay: this trigger fires every frame with no conditions.

Setting priority

It is possible to set a certain trigger's priority like so

register("renderOverlay", myRenderOverlayLast).setPriority(Priority.LOWEST);
register("renderOverlay", myRenderOverlayFirst).setPriority(Priority.HIGHEST);

function myRenderOverlayLast() {


function myRenderOverlayFirst() {


Here, were are dealing with the priority of triggers. Priorities are LOWEST, LOW, NORMAL, HIGH, HIGHEST. Triggers with a priority of HIGHEST are ran first, because they have first say on an event. Triggers with a priority of LOWEST are ran last. The function lan rast will draw on TOP of anything before it.

Simple text rendering

You can render text onto the screen with this code

register("renderOverlay", myRenderOverlay);

function myRenderOverlay() {
  Renderer.drawString("Hello World!", 10, 10);

Every frame, the code inside myRenderOverlay is called. Inside of this function, we make one call to Renderer.drawString(text, screenX, screenY). We make the text say "Hello World!", and place it on the screen at 10, 10 (the top left corner).

More complex text rendering

This is how you would draw the same string (but colored) with an object

register("renderOverlay", myRenderOverlay);

const myTextObject = new Text("Hello World!", 10, 10).setColor(Renderer.RED);

function myRenderOverlay() {

Here, instead of simply making a method call, we are instatiating an object to do our drawing. This allows for much greater customization, such as rotation, scaling, and as described below, coloring.

The other interesting part to take a look at is the call to setColor, which will, as you can guess, set the color of the text. For the color, we use a preset color in Renderer. We could have also made a call to Renderer.color(red, green, blue, alpha), and subsequently passed that in to the method. In this example, that call would be Renderer.color(255, 255, 255, 255). Values should range from 0-255.

Rendering of shapes

This example renders a rectangle, circle, and triangle

register("renderOverlay", myRenderOverlay);

const rectangle = new Rectangle(Renderer.WHITE, 10, 10, 50, 50);
const circle = new Shape(Renderer.WHITE).setCircle(100, 100, 25, 360);
const polygon = new Shape(Renderer.WHITE)
  .addVertex(300, 300)
  .addVertex(400, 400)
  .addVertex(200, 400);

function myRenderOverlay() {

Let's look at more complex rendering using shapes. We can see our first complex piece of rendering code to the right. The first thing to notice is how we define the shapes outside of our render trigger. This way we aren't create three objects 60+ times a second.

The different shape classes

We create the first shape, the rectangle, with the instantiation of the Rectangle class, whose constructor takes the following arguments: color, x, y, width, and height.

The next shape is a circle, which we create through the more general Shape class, which is just a collection of (x, y) points to connect together. We use the setCircle method, which automatically populates the Shape's vertices to give us a perfect circle.

Finally, we manually configure the vertices of the last shape ourselves with the addVertex method. After we have created all of our shapes outside of the trigger function, we call the draw method inside of the trigger function to render them to the screen.

Rendering images

This example renders the images on the screen

register("renderOverlay", myRenderImageOverlay);
const image = new Image("ctjs-logo.png", "");

function myRenderImageOverlay() {
  image.draw(100, 100);

As before, we register for a render overlay trigger so we can do our rendering in it. However, this time, we create an Image object with the file name and URL. The file will download to the ChatTriggers assets directory, which defaults to .minecraft/config/ChatTriggers/images.

Advanced rendering

Here we are rendering text that is a rainbow color

register("renderOverlay", myRenderOverlay);

const text = new Text("Rainbows!", 10, 10);

let exampleImportStep = 0;
function myRenderOverlay() {


This topic covers advanced rendering, like rainbow colors and dynamic positioning.

Rainbow colors

Again, we setup the default rendering scheme of a RenderOverlay trigger and its corresponding function. However, this time we also create a "exampleImportStep" variable that starts of at 0. Then, every time we render to the screen, we increment this step variable by 1.

This variable is used when it is passed into the Renderer.getRainbow(step) method, which produces rotating colors, which we then use as the color for the drawString method.

Dynamic positioning

This example showcases how to make render positioning dynamic

register("renderOverlay", myRenderOverlay);

const width = Renderer.screen.getWidth();
const rectWidth = 50;
const textStr = "White Text!";

const rectangle = new Rectangle(Renderer.WHITE, width / 2 - rectWidth / 2, 200, rectWidth, 50);
const text = new Text(textStr, width / 2 - Renderer.getStringWidth(textStr) / 2, 100).setColor(Renderer.WHITE);

function myRenderOverlay() {

Here we are making all of our rendered objects be perfectly aligned horizontally on the screen for all windows sizes. We start off by getting the height of the current window, with the call to Renderer.screen.getWidth().

Then, for each part we render, we get half the width of the window, and then subtract half the width of our rendered object. For a string, this is done with (width / 2) - (Renderer.getStringWidth(textToRender) / 2). For a fixed width object, you can replace Renderer.getStringWidth(textToRender) with the width of the object. Notice how we use the Text object and instantiate it outside of the render function. The Text object allows you to set additional properties, such as shadow and color.


The Tessellator object is meant for rendering in 3 dimensions. You have to call this object under the renderWorld trigger.

Drawing Text

You can set floating text at specific world coordinates using Tessellator.drawString(text, x, y, z)

register("renderWorld", myWorldRender);

function myWorldRender() {
  Tessellator.drawString("Hello there!", 100, 100, 100);

This will draw the words "Hello There" at the world location (100, 100, 100). You can also change the text color and scale, whether or not to render the shadow, and whether or not the size increases from distance.

Drawing Textures

You can also draw specific textures like so

register("renderWorld", myWorldRender);

const img = new Image("kerbybit.png", "");

function myWorldRender() {
    .translate(Player.getX(), Player.getY(), Player.getZ())
    .pos(-0.5, 0.5, -0.5).tex(0, 0)
    .pos(-0.5, 0.5, 0.5).tex(0, 1)
    .pos(0.5, 0.5, 0.5).tex(1, 1)
    .pos(0.5, 0.5, -0.5).tex(1, 0)

This will draw kerbybit's skin texture at the player's feet position. This works by first binding the texture of the image to the Tessellator object. It then associates positions in the 3D world (relative to the Tesselator's current position) to points on the texture. The tex function takes an x and y argument, both ranging from 0.0 to 1.0, which identifies a point on the texture (i.e. .tex(0.3, 0.7) refers to the point 30% across from the left and 70% down from the top). Finally, a call to .draw() finished the operation.

Partial Ticks

Partial ticks are a fractional value representing the amount of time that’s passed between the last full tick and now. This is useful in rendering, as the rest of the game runs on the tick system, but rendering uses frames, a much more precise operation. We use this because otherwise the animation would be jittery because there are fewer ticks per second than frames per second.

If we wanted to make the example from above not be jittery, we would use partial ticks.

const myPlayerMP = new PlayerMP(Player.getPlayer());

function myWorldRender(partialTicks) {
  const lastX = myPlayerMP.getLastX();
  const lastY = myPlayerMP.getLastY();
  const lastZ = myPlayerMP.getLastZ();

  const currentX = Player.getX();
  const currentY = Player.getY();
  const currentZ = Player.getZ();

      lastX + (currentX - lastX) * partialTicks,
      lastY + (currentY - lastY) * partialTicks,
      lastZ + (currentZ - lastZ) * partialTicks
    .pos(-0.5, 0.5, -0.5).tex(0, 0)
    .pos(-0.5, 0.5, 0.5).tex(0, 1)
    .pos(0.5, 0.5, 0.5).tex(1, 1)
    .pos(0.5, 0.5, -0.5).tex(1, 0)

The key difference here is that the change in position is multiplied by the change in time every frame. This makes the drawing smooth since it updates every frame instead of every tick.


ChatTriggers provides several objects to expand the functionality of your imports without you needing to delve into base Minecraft code. A list is found below.

Object Description
Book Makes an openable book in Minecraft
CPS Contains information about the player's clicks per second
Display Renders text on to the game screen
Gui Makes an openable gui in Minecraft
Inventory Contains information about the player's inventory
KeyBind Used for detecting a key's state
ParticleEffect Allows creation of custom particle effects to be displayed client side
Player Used for getting information about the player
Thread This is a pseudo object, used to do tasks that take a long time


Book objects are used for displaying base Minecraft book GUIs with customizable text.


This is how you create a book

const book = new Book("Example Book");

We create our book with the Book constructor of new Book(bookName);. We want to create our book in the global scope, as explained below.

Adding content

This is how you add pages to the book

const book = new Book("Example Book");

book.addPage("This is a very simple page with just text.");
book.addPage(new Message("This is a page with a ", new TextComponent("twist!").setHoverValue("Hi! I'm hover text :o")));

To add content to our book, we'll want to utilize the .addPage(message) method. This can take either a simple string as the message for the page, or a Message object if you want to utilize the functionality the provide, covered here. This should be done right after the instantiation of the book.

Updating content

This is how to update a page's content

book.setPage(1, new Message("lol!"));

To set a page, we use the .setPage(pageNumber, message). Page number is the number of the page you wish to update, 0 based. The message has to be a Message object, there is no method for just a string.

This can be done anytime, just re-display the book to see the updated version. The page you try to set must already exist, or else there will be errors. Just add the page if you need to add a new page afterwards.


This is how to display the book to the user


This is how to display the book starting on a certain page


This is a very simple operation which just opens the book. You can also specify a page number to open the book to as the first argument, it defaults to 0. If the page you specify doesn't exist, the player will have to click one of the arrows to go to the next available page.


The CPS object gives information about the player's clicks per second.

Clicks per second

To get the left clicks per second, use this

const leftClicks = CPS.getLeftClicks();

To get the right clicks per second, use this

const rightClicks = CPS.getRightClicks();

There are more methods for the CPS object, but these are the most common. You can always see a full list of up to date documentation on the JavaDocs.


Displays are used for rendering simple text on to the players screen. If you would like to utilize other rendering functions found in the rendering section, use custom rendering functions.


This is how you can create a Display object

const display = new Display();

This Display object is now created, but it doesn't do much of anything yet.

Adding content

This is how you can add lines to a display

display.addLine("Ay! First line.");
display.addLines("2nd line", "3rd line");
display.addLine(new DisplayLine("2 line gap above me!"));

Displays consist of lines of text. These lines can be added and set, and they can use color codes. The first call to .addLine(message) adds a line to the display with the text passed in. The second call to .addLines(messages...) adds as many lines as you pass into it, in our case just 2. If you don't pass in Strings or DisplayLines, then it will just add one line per parameter you passed (e.g .addLines(2) will only add 1 empty line despite the number, but .addLines(0, 0) will add 2 empty lines due to there being 2 parameters).

Setting content

This is how you set a line in a display

display.setLine(3, "Now this line has text :)");

In this example the call to .setLine(lineNumber, message) sets the 4th line in the display (0 based) which was previously blank to our example text. This is what we would use if we want to update a display with information, like the player's current coordinates.

Setting positioning

This is how you set the alignment of a display


This aligns your display on the left side of the screen. Other options are "center" and "right".

This is how you set the order of the lines. Both of these ways will work, setting the order to go down.


This renders the lines from 0 going downwards, usually what you'd want. The other option is "up".

This is how you set the exact position of the display

display.setRenderLoc(10, 10);

This sets the X and Y coordinate of where your display should start, with the first argument being X, and the second being Y.

Setting background and foreground options

This sets the background color of the display


This makes the background color of the display aqua. Other options are all the colors in Renderer, or a custom color with Renderer.color(r, g, b, a).

This sets the type of background for the display to be fit per line. Both of these options will work.

display.setBackground("per line");

This option sets how the background color should be displayed. PER_LINE says that the background should be the width of each line. NONE would mean don't show background color, and FULL would indicate make the background color draw in a box around the entire display.

This sets the foreground (text) color of the display


All text in the display will now show blue. This method can take any Renderer color, including custom ones described above.

DisplayLine can also be used to set the text color, background color, and alignment for a specific line.

  new DisplayLine("This will have green text!").setTextColor(Renderer.GREEN)

This will set the new line to have green text, overriding the blue we set earlier.


Guis are screens that are opened in game, such as the chat gui, or the escape menu. These stop the player from moving.


This is how you create a gui

const gui = new Gui();

Like other objects, creating a Gui is very simple.

Rendering the gui

This is how you set up a function to render the gui


function myGuiRenderFunction(mouseX, mouseY, partialTicks) {
  Renderer.drawRect(Renderer.WHITE, mouseX, mouseY, 50, 50);

Everything inside of the "myGuiRenderFunction" will be ran while the gui is open. Inside of this function you should make use of the Renderer functions to draw what you want on to the screen. The three arguments passed in are the x coordinate of the user's mouse, the y coordinate of the users mouse, and the partial ticks.

In this example, we render a 50x50 square with the top left corner being the user's mouse position.

Adding interactivity

This is how you detect when a user presses a key


function myGuiKeyTypedFunction(typedChar, keyCode) {"You typed " + typedChar);

This is how you detect when the user clicks


let renderSquareX = 0;
let renderSquareY = 0;

function myGuiRenderFunction(mouseX, mouseY, partialTicks) {
  Renderer.drawRect(Renderer.WHITE, renderSquareX, renderSquareY, 50, 50);

function myGuiClickedFunction(mouseX, mouseY, button) {
  renderSquareX = mouseX;
  renderSquareY = mouseY;

In the first example we register our key typed function to be activated when someone types a key in our Gui. The keyCode passed can be compared with the Keyboard class keys.

In the next example we make things more complicated. We register both a draw function and a mouse clicked function. We render the same box as in the previous draw example, however, we make the coordinates of it be the last place the mouse was clicked.

Displaying the gui

To display the gui, use this;

This opens the gui...

To close the gui, use this


...and this closes the gui.

These very simple methods open and close the gui, and neither take any arguments.


The Inventory object contains methods used for getting information about the user's inventory. It can be called through Player.getInventory(). More about the Player class can be found later.


function hasSponge() {
  const inventory = Player.getInventory();

  // The ID for sponge is 19.
  const spongeSlot = inventory.indexOf(19);

  if (spongeSlot !== -1) {"Sponge found in slot " + spongeSlot + "!");
  } else {"Sponge not found!");

The example above lets us see if the inventory has a sponge item in it, and if so, say the slot it's in. This works by first getting the inventory of the player, then it gets the slot ID of sponge, if there is any. If any slot has a sponge, it will output the slot, otherwise it will return -1.


KeyBinds are used for detecting the state of a key.


This is the preferred method to get a keybind

const wKeyBind = Client.getKeyBindFromKey(Keyboard.KEY_W, "My W Key");

Let's break this down. We call the function Client.getKeyBindFromKey and save the result in a variable. This will result in trying to find an existing Minecraft keybinding in order to try not to override it. If there isn't one being used, it will create a new KeyBind, with the description and key we specified. We pass into this a keyCode from the Keyboard class.

In our case, this will return the keybind already used by Minecraft for the run key (unless of course you changed yours to a different key).

Using the keybind

To check if they key bind is being held, do this

if (wKeyBind.isKeyDown()) {"Key is down!");

To check if the key bind was pressed, use this

if (wKeyBind.isPressed()) {"Key is pressed!");

This first example would spam your chat if it was in an Tick trigger or something of the like, as it will always be true if you are holding down the key.

Now, in the second example, we would only get the chat message once every time we press and hold the key. It only returns true one time per key press. If you let go and press again, it will return true once more.


The Player object contains many methods used for retrieving information about the player.


To get the direction the player is facing, use this

const direction = Player.facing();

To get the coordinates of the player, you can use this

const playerX = Player.getX();
const playerY = Player.getY();
const playerZ = Player.getZ();

Using this in conjunction with displays, you can make a coordinates HUD.

register("tick", locationTracker);

const display = new Display();
display.setRenderLoc(10, 10);

function locationTracker() {
  display.setLine(0, "X: " + Player.getX());
  display.setLine(1, "Y: " + Player.getY());
  display.setLine(2, "Z: " + Player.getZ());

In this case, we just made a display, and every tick it updates to show the player's location.


The LookingAt object was replaced with the Player.lookingAt() method.

This gets the current object that the player is looking at, whether that be a block or an entity. It returns either the Block, Sign, Entity class, or an air Block when not looking at anything.

Health and Various Stats

You can not only get the player's health, hunger, active potion effects, but much more! Here's just a few examples of how to get each part.

const health = Player.getHP();
const hunger = Player.getHunger();
const potions = Player.getActivePotionEffects();
const xpLevel = Player.getXPLevel();


To get the player's armor, you can use Player.armor

const helmet = Player.armor.getHelmet();

This would return the item that is in the helmet slot of the player. This can be a pumpkin or any item that is on the helmet slot.

Getting Inventory information

Here's an example showing how to find the durability and stack size of the item the player is holding.

function displayHeldItemInfo() {
  const item = Player.getHeldItem();

  if (item.getName() !== "") {
    let durabilityPercentage = Math.ceil(
      (item.getDurability() / item.getMaxDamage()) * 100

    // If NaN, that means it's a block
    if (isNaN(durabilityPercentage)) durabilityPercentage = "N/A (not a tool!)";"Item: " + item.getName());"Durability: " + durabilityPercentage);"Stack Size: " + item.getStackSize());
  } else {"&4You aren't holding anything!");

In this case, we get the held item of the player. If the item isn't air, then it finds how damaged it is. If the durability isn't a number, that means the item has to be a block. Then, if the player isn't holding anything, it runs the last piece of the code, which is when the hand is empty. If you want to get the player's inventory as a list of Items, use Player.getInventory().