Scripting reference

The .j17 language

A safe scripting language embedded in the June 17 game maker. It adds what point-and-click rules can’t say on their own — scoped variables, arithmetic, branching, loops, real-time elapsed tracking, and sprite graphics with time-based animation — while staying impossible to misuse: a fixed vocabulary, no access to the page, and a hard step budget so every script is guaranteed to finish.

3 variable scopessprite move · fade · scale · spintweened animationserver-anchored timeno DOM · files · network

01What it is

Most of a June 17 game is built from declarative rules you set in the editor — “this hotspot gives that item”, “this exit needs that flag”. .j17 is the small escape hatch for the handful of moments those rules can’t reach: keeping a count, doing arithmetic, choosing between outcomes, or repeating an action a set number of times.

It is deliberately not general-purpose JavaScript. There is a fixed set of words, no way to touch the page, the file system, or the network, and a hard limit on how long a script may run. That makes any .j17 script safe to share inside a published game.

02Where scripts run

A script is a script effect — one of the actions you can attach to an interaction. It fires when the player triggers that interaction: examining or using a hotspot, or reaching a line or choice in a conversation. The script runs once, top to bottom, then control returns to the game.

Hotspot & item usetrigger

Attach a script to what happens when the player interacts with a hotspot or combines an item — the same place you'd give an item or set a flag.

Dialogue lines & choicestrigger

A conversation line or a player choice can carry a script, so a single answer can update several things at once.

Three verbs hand off and stop

goto, win, and minigame transfer control to the engine. Any statements after them in the same run are not executed — put them before the hand-off, or inside the branch that shouldn’t hand off.

03Syntax basics

One statement per line — or separate several with a semicolon. Text is written in double quotes. Anything after # on a line is a comment. Keywords are lowercase.

# a comment runs to the end of the line
set tries = tries + 1          # one statement per line
give coin; sfx coin            # …or several, split by ;
say "Text always uses double quotes."

Names vs. text

Item, room, flag, sound and mini-game names may be written bare (coin) or quoted ("blue key" — quote it when the name has a space). The words after say are spoken text and must always be quoted.

04Statements

The complete vocabulary — the verbs that change the game’s state, speak, play a sound, or move the story on.

set name = exprvariable

Store a number or string in a variable. Unset variables read as 0. Saved to the player’s game the moment it changes.

set gold = gold + 5
give iteminventory

Add an item to the player's inventory.

give bracelet
take iteminventory

Remove an item from the inventory.

take kite
flag namestory flag

Set a story flag on. Flags are the same ones the rest of the game reads — use them to unlock exits or reveal hotspots gated with showWhen.

flag met_mia
unflag namestory flag

Turn a story flag back off.

unflag door_open
say exprdialogue

The character the player is interacting with speaks a line. Any text expression — join in live values with + and str().

say "You have " + str(gold) + " gold."
sprite name …graphics

Move, fade, scale, spin, show or hide a sprite in the current scene — instantly or animated over time. Its own section covers it in full.

sprite "ghost" fade 1 over 5
sfx soundsound

Play a built-in sound effect by name (for example coin).

sfx coin
score exprleaderboard

Report a number to this game’s June 17 leaderboard — the player’s best is kept. Enable the leaderboard first on the game’s manage page. See Scores & achievements.

score gold
achievement "key"milestone

Unlock a creator-defined achievement by its key (define the key on the manage page). Unlocking is idempotent — safe to call again.

achievement "first_win"
goto roomstops script

Travel the player to a room, then end the script and hand control to the engine.

goto cellar
minigame gamestops script

Launch a mini-game, then end the script.

minigame reels
winstops script

Trigger the game's ending / win screen, then end the script.

win

05Control flow

Two structures: a conditional and a counted loop. Both close with end.

if expr thenelseendconditional

Run the first block when the expression is true (any non-zero number). The else block is optional.

if has(ticket) and gold >= 10 then
  take ticket
  goto fairground
else
  say "Not yet — you need a ticket and ten gold."
end
repeat expr … endloop

Repeat the block a set number of times (the count is rounded down to a whole number and capped at 10,000). It runs to completion instantly, so it's for repeating state changes — not for animating over time.

repeat 3
  give coin
end

06Expressions

Expressions produce a number or a string. They power set and the condition in if and repeat. There are three built-in queries and the usual arithmetic, comparison, and logic operators.

has(item)query · fn

1 if the item is in the inventory, else 0.

if has(map) then say "You know the way." end
flag(name)query · fn

1 if the story flag is set, else 0. (As a query it takes a name in parentheses; as a statement, flag sets one.)

if flag(rescued_dog) then win end
charquery

The id of the currently active party character (a string). Compare it to branch on who's in control.

if char == "lac" then say "I remember this." end
literals & variablesvalues

Numbers (5, 2.5), quoted "strings", and true / false (which are just 1 and 0). A bare word is a variable; unset ones are 0.

set ready = true
set who = "stranger"
math functionsfn

floor · round · abs · sqrt · sin · cos (radians) · min(a,b) · max(a,b) · random() (0–1). Enough to shape a curve or a wave.

set y = 200 + sin(t) * 40
text & time functionsfn

str(x) turns a number into text; + joins text (so say can show live values). now() and timeset(v) read the clock — see Time.

say "Score: " + str(score)
operatorsprecedence: low → high

Logic: or, then and, then not.   Comparison: == != < > <= >=.   Arithmetic: + -, then * / %.   When either side of + is text it joins instead of adding. Use ( ) to group and -x to negate. Dividing by zero yields 0.

set score = (base + bonus) * 2 - penalty
if not flag(warned) and tries > 2 then  end

07Variables & scope

A variable’s name says where it lives and how long it lasts. Three scopes cover everything from a one-scene counter to a value that follows the player across saves.

name  or  game.nameglobal · saved

Global & permanent. Readable and writable from any scene, saved with the player’s game, and reset only when they start a New Game from the menu. Every change also records when it happened (see Time). A bare name is the same as game. — this is the default.

set game.day = game.day + 1
temp.nameglobal · not saved

Global but temporary. Shared across scenes for the length of a session, but never written to the save — it starts empty each time the player opens the game. Good for “what happened a moment ago”.

set temp.last_room = "forest"
local.namethis scene only

Scene-local. Belongs to the current room and is cleared the moment the player leaves it. Perfect for a per-puzzle counter you don’t want leaking elsewhere.

set local.tries = local.tries + 1

Everything is shared with the game world

Flags and inventory a script touches are the same ones your hotspots and exits read — set a flag here, gate a door with it elsewhere.

08Time & elapsed

Every global (saved) variable remembers the moment it was last changed. Combined with the current time, that lets you tell the player how long it’s been — “123 days since you arrived on the island”.

now()seconds

The current time, in seconds. Anchored to an authoritative server time when the game starts and advanced by a monotonic clock — so a player can’t fast-forward it by changing their device clock.

timeset(name)seconds

The time a global variable was last set (0 if it never was). Subtract it from now() to get elapsed seconds.

“123 days since you arrived”pattern

Set a marker global once (that stamps the time), then compute the difference whenever you need it — 86,400 seconds to a day.

# the moment they arrive, once:
set game.arrived = 1

# anywhere later:
set days = floor((now() - timeset(game.arrived)) / 86400)
say "It's been " + str(days) + " days since you arrived."

09Sprites & animation

A script can take any named sprite in the current scene and change how it looks — position, transparency, size, rotation — either instantly or animated over a span of seconds. The shape is always the same:

sprite "name"  action args  [over seconds]  [after seconds]

Leave off over and the change is instant. Add over 3 and it eases smoothly to the target across three seconds; add after 1 to hold for a second first. Several animations on the same sprite run together, so moving and fading at once reads as one motion.

move x yposition

Place the sprite (its position in scene coordinates). Animate it to glide.

sprite "crow" move 480 120 over 2
fade alphatransparency

Opacity from 0 (invisible) to 1 (solid).

sprite "ghost" fade 0.4 over 3
scale factorsize

Size, where 1 is the sprite’s normal size.

sprite "balloon" scale 1.5 over 4
spin degreesrotation

Rotation in degrees, around the sprite's centre.

sprite "key" spin 360 over 1
show · hidevisibility

Reveal or remove a sprite. With over, they fade in or out instead of popping.

sprite "door" hide over 0.5
timingover · after

over N sets the duration; after N delays the start — chain a few with different after values to sequence a little scene.

“Can I pick a sprite and make it move on a sine-wave path while its transparency changes — like a ghost drifting out of the forest and slowly becoming visible?”

✓ Yes — that’s exactly what this is for.

# the ghost starts hidden, deep in the trees
sprite "ghost" fade 0
sprite "ghost" move 120 300

# over 5s it drifts forward and materialises — both at once
sprite "ghost" move 300 240 over 5
sprite "ghost" fade 1 over 5

For a true wave you can drive a position from sin() — set up a marker and nudge it, or compute a y from a rising counter. A single tween eases in a graceful arc on its own; sin() gives you a full ripple when you want one.

10Safety & limits

The language is sandboxed on purpose, so a script inside a shared game can never do harm or hang.

Always terminates200,000 steps

Every run has a step budget. Loops that would run away simply stop — a script can't freeze the game.

Loops are bounded≤ 10,000

A repeat count above 10,000 is capped, and negative counts run zero times.

No reach outside the gamesandboxed

No page, file system, network, or arbitrary code — only the fixed set of verbs on this page.

Checked as you writevalidated

The editor validates the script and reports the first syntax error, so a broken script never ships.

11Examples

Whole scripts, the way you’d attach them to a hotspot or a line of dialogue.

A lock that opens on the third trycounter · branch
# attached to "use the rusty lock"
set tries = tries + 1
if tries >= 3 then
  sfx coin
  say "The lock finally gives."
  goto cellar
else
  say "Stuck. Maybe once or twice more."
end
A simple tradeinventory
if has(kite) then
  take kite
  give bracelet
  sfx coin
  say "A fair trade."
else
  say "Bring me the kite first."
end
Hand over a small rewardloop
# repeat is for repeating an action, not animating
repeat 5
  give coin
end
say "Five coins for the road."
Branch on who's controllingchar
if char == "lac" then
  flag lac_remembered
  say "You've stood here before."
else
  say "It means nothing to you — yet."
end

12Boundaries

.j17 reaches a lot further than it used to, but it’s still a scripting layer over the game — not a drawing engine. The honest edges:

Sprites must already existscene

You animate sprites you've placed in the scene in the editor — a script can't conjure a brand-new image out of nothing. A hidden, pre-placed sprite is the usual starting point.

Current scene onlyscope

sprite acts on the room the player is in. It can’t reach into another room’s sprites.

Animations aren't savedvisual state

A sprite's position, fade and spin are live visual state — they reset when the player leaves the room or reloads. Persist the story with variables and flags; re-run the animation on the way back in.

No free-form drawing or audioout of scope

No arbitrary shapes, pixels, or filters, and sound is limited to the game’s built-in effects via sfx. And, as always: no page, files, or network.

← Developer · The embedded logic language of the June 17 game maker. Reference generated from the engine’s own source.