Summer 2026 Music Kit Competition is now here! - Create a summer themed electronic music kit and submit it by - Enter Now!
Ready-made recipes for things mappers ask for most. Every recipe is a complete, working script: copy it into a .js file, point a lambda_point_script at it, and tweak from there. If any of the code looks unfamiliar, JavaScript Basics explains it all.
The "hello world" of map scripting. In Hammer, add OnPressed → map_script → OpenDoor to your button.
import { Instance } from "lambda_script/point_script";
Instance.OnScriptInput("OpenDoor", () => {
Instance.EntFireAtName({
name: "security_door",
input: "Open",
});
});
EntFireAtName can fire any input at any named entity: Open, Unlock, Trigger, Enable, whatever the target accepts. It's your general-purpose replacement for output chains.
Where scripting starts beating math_counter:
import { Instance } from "lambda_script/point_script";
let presses = 0;
Instance.OnScriptInput("PressButton", () => {
presses++;
if (presses === 3) {
Instance.EntFireAtName({ name: "vault_door", input: "Open" });
} else {
Instance.Msg(`${3 - presses} presses to go`);
}
});
import { HUD, Instance } from "lambda_script/point_script";
Instance.OnScriptInput("AnnounceStart", () => {
Instance.PrintMessage(HUD.PRINTCENTER, "The event has begun!");
});
Swap HUD.PRINTCENTER for HUD.PRINTTALK (chat) or HUD.PRINTCONSOLE (console). To message one specific player, call player.PrintMessage(...) on a player entity instead.
There is no setTimeout in map scripting. For a one-off delayed entity input, use the built-in delay option:
Instance.EntFireAtName({
name: "explosion_effect",
input: "Trigger",
delay: 3, // seconds
});
For delayed code, use Instance.SimpleTimer:
import { Instance } from "lambda_script/point_script";
Instance.OnScriptInput("StartCountdown", () => {
Instance.SimpleTimer(3, () => {
Instance.Msg("Three seconds have passed!");
});
});
Instance.CreateTimer runs a callback on a repeating interval. This countdown runs exactly ten times, one second apart:
import { HUD, Instance } from "lambda_script/point_script";
let secondsLeft = 10;
Instance.CreateTimer({
delay: 1,
repetitions: 10,
callback: () => {
Instance.PrintMessage(HUD.PRINTCENTER, `Starting in ${secondsLeft}...`);
secondsLeft--;
if (secondsLeft === 0) {
Instance.EntFireAtName({ name: "arena_door", input: "Open" });
}
},
});
Set repetitions: 0 to repeat forever. The returned timer object can be paused, resumed, and removed, so one script can run as many independent timers as it needs; see the timer reference for everything a Timer can do.
import { Instance } from "lambda_script/point_script";
Instance.OnScriptInput("HighlightObjective", () => {
const objective = Instance.FindEntityByName("mission_objective");
objective?.Glow({ r: 255, g: 180, b: 0 }); // Orange glow.
});
Instance.OnScriptInput("ClearHighlight", () => {
Instance.FindEntityByName("mission_objective")?.Unglow();
});
The ?. means "only if the entity was found", so a typo in the name does nothing instead of causing an error.
import { Instance } from "lambda_script/point_script";
Instance.OnScriptInput("TeleportAll", () => {
for (const player of Instance.GetAllPlayerControllers()) {
player.Teleport({
position: { x: 0, y: 512, z: 64 },
velocity: { x: 0, y: 0, z: 0 }, // Stop their momentum too.
});
}
});
Coordinates are the same world units you see in Hammer's status bar.
import { Instance } from "lambda_script/point_script";
Instance.OnPlayerChat(({ player, text }) => {
if (text === "!dance") {
Instance.EntFireAtName({ name: "disco_lights", input: "Toggle" });
Instance.Msg(`${player.GetPlayerName()} started the disco`);
}
});
import { Instance } from "lambda_script/point_script";
Instance.OnScriptInput("ArmEveryone", () => {
for (const player of Instance.GetAllPlayerControllers()) {
if (player.IsAlive() && !player.HasWeapon("weapon_ak47")) {
player.GiveWeapon("weapon_ak47");
}
}
});
HasWeapon stops a player collecting duplicates, and player.SelectWeapon("weapon_ak47") switches them to it. To take a weapon away again, use player.StripWeapon("weapon_ak47"). See Player Methods for the full list.
OnPlayerLoaded fires once a player has fully loaded their profile, which makes it the safest "player has arrived" moment:
import { HUD, Instance } from "lambda_script/point_script";
Instance.OnPlayerLoaded((player) => {
player.PrintMessage(HUD.PRINTTALK, `Welcome to ${Instance.GetMapName()}, ${player.GetPlayerName()}!`);
});
import { Instance } from "lambda_script/point_script";
let jumps = 0;
Instance.OnPlayerJump((player) => {
jumps++;
Instance.Msg(`${player.GetPlayerName()} jumped (${jumps} total)`);
});
Instance.OnPlayerLand((player) => {
Instance.Msg(`${player.GetPlayerName()} landed`);
});
Useful for custom mechanics. This one only lets ducking players through a door:
import { Input, Instance } from "lambda_script/point_script";
Instance.OnScriptInput("TryEnter", () => {
const player = Instance.GetPlayerController(0);
if (player?.IsInputPressed(Input.DUCK)) {
Instance.EntFireAtName({ name: "crawl_door", input: "Open" });
}
});
Combine flags with | to match any of several inputs: Input.ATTACK | Input.ATTACK2.
All variables reset when the map restarts. Save data survives. It's one text string stored per map, so the usual trick is keeping an object in it as JSON:
import { Instance } from "lambda_script/point_script";
const state = JSON.parse(Instance.GetSaveData() || "{}");
Instance.OnScriptInput("SecretFound", () => {
state.secretsFound = (state.secretsFound ?? 0) + 1;
Instance.SetSaveData(JSON.stringify(state));
Instance.Msg(`Secrets found across all sessions: ${state.secretsFound}`);
});
On Counter-Strike servers the CS API adds round and gameplay events:
import { CS, CSRoundEndReason, CSTeam, HUD, Instance } from "lambda_script/point_script";
CS.OnRoundStart(() => {
Instance.EntFireAtName({ name: "map_reset_relay", input: "Trigger" });
});
CS.OnRoundEnd((winningTeam, reason) => {
if (winningTeam === CSTeam.CT && reason === CSRoundEndReason.TARGET_SAVED) {
Instance.PrintMessage(HUD.PRINTCENTER, "The bomb was defused!");
}
});
CS.OnBombPlant((plantedC4, planter) => {
Instance.Msg(`${planter.GetPlayerName()} planted the bomb`);
});
CS only exists when the Counter-Strike gamemode is running. If your script might run elsewhere, see optional APIs for how to check availability safely.
Set up a point_template in Hammer with your entity as its template, then spawn copies on demand:
import { Instance, PointTemplate } from "lambda_script/point_script";
Instance.OnScriptInput("SpawnCrate", () => {
const spawner = Instance.FindEntityByName("crate_template");
if (spawner instanceof PointTemplate) {
const spawned = spawner.ForceSpawn();
spawned?.[0]?.Glow({ r: 0, g: 255, b: 0 });
}
});
Sprinkle Instance.Msg calls to trace what runs and when, draw debug shapes in the world, or register a console command you can trigger by hand (requires sv_cheats 1):
import { Instance } from "lambda_script/point_script";
Instance.RegisterCheatCommand("test_door", () => {
Instance.EntFireAtName({ name: "security_door", input: "Open" });
});
Instance.DebugBox({
mins: { x: -16, y: -16, z: 0 },
maxs: { x: 16, y: 16, z: 72 },
duration: 10,
color: { r: 0, g: 255, b: 0, a: 64 },
});
More debugging tools are covered in Scripting: In-Depth.