Summer 2026 Music Kit Competition is now here! - Create a summer themed electronic music kit and submit it by - Enter Now!
This page teaches you just enough JavaScript to script a map. It assumes you know Hammer, but have never written code. If you already know JavaScript, skip ahead to the Scripting Cookbook.
Every example below works inside a real script file, so feel free to paste them in and experiment. Remember that every script starts with the import line:
import { Instance } from "lambda_script/point_script";
Anything after // on a line is a comment: a note to yourself that the game ignores. Use them generously.
// This line does nothing. The next line prints a message.
Instance.Msg("Hello!"); // Comments can also follow code.
A variable is a named box that holds a value. Think of it like a math_counter, except it can hold text, numbers, lists, or anything else.
let pressCount = 0; // A number the script can change later.
const doorName = "vault_door"; // A value that never changes.
let when the value will change (counters, flags, timers).const when it never will (names, settings). If you're unsure, start with const; the editor will tell you if you try to change it.Change a variable with =, and do maths with the usual symbols:
pressCount = pressCount + 1; // Add one...
pressCount++; // ...or the short version of the same thing.
const mapAuthor = "ZeRo"; // Text, called a "string". Always in quotes.
const maxRounds = 12; // A number.
const isUnlocked = false; // A boolean: true or false. Like a logic_branch.
To build text out of other values, use a template string: backticks (`) instead of quotes, with ${...} around anything you want inserted:
Instance.Msg(`The door has been opened ${pressCount} times`);
A function is a reusable block of code with a name, the scripting equivalent of a logic_relay. Define it once, then "fire" (call) it whenever you like:
const openVault = () => {
Instance.Msg("Opening the vault");
Instance.EntFireAtName({ name: "vault_door", input: "Open" });
};
openVault(); // Runs the two lines above.
Functions can take inputs of their own, called parameters:
const announce = (message) => {
Instance.Msg(`[Announcement] ${message}`);
};
announce("Round starting soon");
The (...) => { ... } style is called an arrow function. It's the style used throughout these docs.
In Hammer, you don't open a door by standing in the trigger yourself. You tell the trigger "when someone touches you, open the door". Scripting works the same way: you hand the game a function and say "run this when X happens". That handed-over function is called a callback.
Instance.OnScriptInput("StartEvent", (value) => {
Instance.Msg("The map fired StartEvent at me!");
});
Instance.OnPlayerChat(({ player, text }) => {
Instance.Msg(`${player.GetPlayerName()} said: ${text}`);
});
Nothing inside the callback runs when the script loads. It runs later, each time the event actually happens. Almost everything in map scripting is registering callbacks like these.
An object groups related values together under one name, using { key: value } pairs, very much like an entity's keyvalues:
const settings = {
doorName: "vault_door",
openDelay: 2,
message: "The vault is opening!",
};
Instance.Msg(settings.message); // Read a value with a dot.
Many scripting functions take an object as their options, which keeps them readable:
Instance.EntFireAtName({
name: "vault_door",
input: "Open",
delay: 2,
});
An array is an ordered list of values, written with square brackets. Use a for...of loop to do something with each entry:
const players = Instance.GetAllPlayerControllers();
for (const player of players) {
Instance.Msg(`${player.GetPlayerName()} is on the server`);
}
if runs code only when a condition is true, the scripting version of a logic_branch or filter:
if (pressCount >= 3) {
Instance.Msg("The button has been pressed enough times!");
} else {
Instance.Msg(`Only ${pressCount} presses so far`);
}
Useful comparisons:
| Written as | Means |
|---|---|
=== |
is equal to |
!== |
is not equal to |
> and < |
greater than / less than |
>= and <= |
at least / at most |
&& |
and (both must be true) |
|| |
or (either can be true) |
Writing if (count = 3) sets count to 3 instead of checking it. Always use === when comparing.
When a scripting function looks for something that doesn't exist (say, an entity name with a typo), it returns the special value undefined rather than causing an error. Always check before using the result:
const door = Instance.FindEntityByName("vault_door");
if (door !== undefined) {
Instance.Msg(door.GetClassName());
}
There is a handy shortcut: ?. (optional chaining) means "only continue if this exists":
door?.Glow({ r: 255, g: 180, b: 0 }); // Does nothing if door is undefined.
If your script does something invalid, it throws an error: the current callback stops and the error is printed to the server console with the line number. That console output is your first stop when debugging. You can also catch errors yourself so the rest of your code keeps running:
try {
const entity = Instance.FindEntityByName("temporary_target");
entity?.Remove();
} catch (error) {
Instance.Msg(`Could not remove entity: ${error}`);
}
Instance.msg(...) is an error; it's Instance.Msg(...). Script input names are case-sensitive too: OpenDoor and opendoor are different inputs.import { Instance } from "lambda_script/point_script"; at the top.( needs a ), every { needs a }, every opening quote needs a closing one. A good editor highlights these instantly; see editor setup.setTimeout. It doesn't exist here. Use Instance.SimpleTimer, or a delay on EntFireAtName. Both are shown in the Cookbook.You now know enough to read every example in these docs. Head to the Scripting Cookbook and start building. Copying a working recipe and tweaking it is the fastest way to learn.