Ship server features while the server is running.
PixelScript runs your logic as JavaScript or TypeScript on the JVM, calling the real Bukkit/Paper API. Save a file and the change is live.No rebuild, no restart, no reconnect.
You are not learning a scripting language. You are writing Java with JavaScript syntax, against the same API you already know, with the iteration loop of a web app. Generated .d.ts definitions mean your IDE autocompletes against the exact Bukkit version you are running.
1import { error, success } from '@/patches/chat/messages';
2
3const PERMISSION = 'dc.command.fly';
4
5registerCommand('fly', (sender, args) => {
6 if (!sender.hasPermission(PERMISSION)) {
7 sender.sendMessage(error('You do not have permission for that.'));
8 return;
9 }
10
11 const enabled = !sender.getAllowFlight();
12 sender.setAllowFlight(enabled);
13
14 sender.sendMessage(
15 success(enabled ? 'Flight enabled.' : 'Flight disabled.')
16 );
17});
18
19// A complete, working feature. Save the file and /fly exists.
20// Delete it and the command is gone, unregistered cleanly.
21Every snippet on this page is taken from a live production server, not written for the brochure.
How it works
There is no build step and no separate process. Your scripts are loaded into the server's own JVM, and a save is a targeted reload rather than a restart.
Your scripts
Plain .js and .ts files on disk, in whatever tree you want. A file watcher notices the moment one changes.
The runtime
TypeScript is compiled, then everything is compiled to JVM bytecode rather than interpreted. The changed script and its dependents are torn down and rebuilt, and their commands, listeners and tasks are unregistered along with them.
The live server
The new version registers itself against the real Bukkit/Paper API. Players stay connected, the world stays loaded, and nothing else on the server is touched.
$ unloaded 1 script, 3 dependents
$ reloaded in 41ms
Because your code runs inside the server, there is no bridge to marshal across and no API subset to work around. Everything a Java plugin can do, a script can do with the same objects, the same events and the same services, reached from JavaScript or TypeScript instead of from a jar you have to rebuild.
What you get
A runtime that behaves predictably on a live server, not a sandbox that falls over the moment your codebase grows past one file.
Live reload
Edits are detected and applied without restarting. Commands, listeners and scheduled tasks are unregistered and re-registered for you.
A real reload tree
Watched scripts are barriers, so changing one feature does not reboot your whole server’s logic. Imported modules cascade to their importers. You decide where the boundaries are.
The full Bukkit API
Bukkit, Scheduler, Sql, DataFile, registerCommand, registerListener and fetch are globals. Everything else is one Script.loadClass away, and any Java library can be pulled in at runtime.
Generated types
Every Java class your scripts touch lands in a generated definitions.d.ts, so your IDE autocompletes against the exact Bukkit version you are running.
A two-way bridge
Java plugins can hold a type-safe proxy to an implementation written in JavaScript, and it never goes stale across reloads.
Observability
Timings are collected for listeners, commands, tasks, queries and class loads, so a slow feature is something you look up rather than something you argue about.
A closer look
The parts you will use every day, with the code you would actually write.
Hot reloading
Save a file and the change is applied. Commands, listeners and scheduled tasks are unregistered and re-registered for you, so nothing is left behind from the version you just replaced.

Every script reports what it cost to load, so a slow reload has an obvious culprit.
1const bar = Bukkit.createBossBar(
2 '☆ Welcome ☆', $.BarColor.BLUE, $.BarStyle.SEGMENTED_6
3);
4
5registerListener($.PlayerJoinEvent, (event) => bar.addPlayer(event.getPlayer()));
6registerListener($.PlayerQuitEvent, (event) => bar.removePlayer(event.getPlayer()));
7
8// Anything the runtime cannot unregister for you gets an unload callback,
9// so a reload never leaves a stale bar floating on someone's screen.
10Script.addUnloadCallback(() => {
11 bar.removeAll();
12 bar.setVisible(false);
13});
14
15// ...and the new version adopts everyone who is already online.
16Bukkit.getOnlinePlayers().forEach((player) => bar.addPlayer(player));Measured, not claimed
The same four workloads, written twice as PixelScript and as Skript, then timed on the same Paper server, in the same JVM, in the same run. Every implementation reports a checksum, and the report refuses to compare them if those disagree.
faster than Skript once the work is the language itself: loops, strings, collections and function calls. Your code is compiled to JVM bytecode and JIT-compiled like anything else, so what is left is interop overhead, not interpretation.
faster than Skript when the Bukkit call dominates. Nobody outruns the server; the gap is not one number and we are not going to pretend it is.
Work dominated by the language
Loops, arithmetic, strings, collections, function calls. Nothing but the runtime.
Arithmetic
150,000 iterationssqrt, multiply, modulo and floating point accumulation. No allocation, no API.
per iteration · PixelScript is 271× faster than Skript
Runtime
10,000 iterationsString building, list append, keyed map put/get and user-defined function calls.
per iteration · PixelScript is 51× faster than Skript
Work dominated by the Bukkit call underneath
Everyone is waiting on the same server code, so everyone converges.
Block writes
4,096 blocksWriting and reading back a 16×16×16 region through the Bukkit block API.
per block · PixelScript is 2.8× faster than Skript
Object churn
2,000 itemsItemStack and ItemMeta creation, display name, lore, Location construction and distance.
per item · PixelScript is 1.9× faster than Skript
Dispatch is subtracted. Each suite is entered through a Bukkit command, which costs the server 100–180 µs before any benchmark code runs, and it is the noisiest measurement in the set. It is measured separately, per language, and taken back out of every figure above instead of being quietly folded into them.
Scale matters more than the ratio. A ratio on top of a few nanoseconds is still a few nanoseconds. Where the gap actually shows up in your MSPT is the language-bound work: a loop that costs 10 ns an iteration instead of 2.7 µs is the difference between a tick you never notice and one you do.
Paper 26.1.2 · Skript 2.16.0 · JDK 25 · 24-core Linux · median of 36 invocations
Documented, publicly
Read the whole thing before you commit to anything. Nothing is behind a login.
Tutorial
Your work environment, project setup and the core concepts, roughly in the order you will need them.
API reference
Every global the runtime binds, with signatures, threading notes and worked examples.
Full documentation
Specifications, tips and the quirks worth knowing before you hit them in production.
Using an AI assistant?
Coding agents already speak JavaScript. What they need is the runtime: the load tree, the globals, the threading rules. One command drops a condensed reference into your project and the full docs into your workspace.
git clone --depth 1 https://github.com/pixelib/pixelscript-docs .pixelscript-docs \
&& cp .pixelscript-docs/CLAUDE.md ./CLAUDE.mdReady to build?
Stop waiting on restarts. Build features in the time it takes to describe them, and fix the ones that are already live.