How the game is drawn
phantom.aj.renderThe screen and sound
How the game is drawn: quality, frame rate, the GPU.
Scripts and plugins
A script has all of it, as phantom.aj.render, and so does a plugin, from ctx.phantom(): the SDK runs in the plugin's own frame. Each call is marked with what it needs: game:read for what the area knows and hears, which "game": "read" in the manifest grants, and game:act for doing things in it as the player, which needs "game": "act" (see Permissions).
Overview
| Member | What it does | Plugins |
|---|---|---|
bloom() | A cheap bloom: a GlowFilter, a blur of the shapes' own outline in one colour. | game:act |
clear() | Takes off everything this area put on: every filter, every hold, and the shader on the card, all back as the client had them. | game:act |
current() | How the game is drawn now: quality, frame rate, scale mode and size, read in one message. | game:read |
filters() | Puts any of Flash's own filters on, in order, and takes them off together: a blur, a drop shadow, a chain of several. | game:act |
frameRate() | stage.frameRate now: 24 is the client's own. | game:read |
grade() | Grades the colours (a ColorMatrixFilter) over the room or the whole game, put back by the loader whenever the client replaces the filters. | game:act |
keepQuality() | Holds stage.quality against the client's own writes: the loader looks every twelfth frame and puts it back. | game:act |
quality() | stage.quality now, or null before the game is up. | game:read |
scaleMode() | How the game fills the window now (stage.scaleMode), or null for one with no name here. | game:read |
setFrameRate() | Sets the stage's frame rate, kept between 1 and 120, and resolves with the rate it now runs at. | game:act |
setQuality() | Sets stage.quality once, and resolves with it once the stage reads it back. | game:act |
setScaleMode() | Sets how the game fills the window: fit (the client's own) shows all of it keeping its shape, stretch fills the window stretched, fill fills it keeping the shape and cuts off the rest, none does not scale. | game:act |
shade() | Runs a Pixel Bender kernel over the game (ShaderFilter), on the processor, every pixel every frame. | game:act |
sharpen() | Sharpens with a three-by-three convolution. | game:act |
smoothAnimals() | Smooths the animals, pets and anything else drawn as a bitmap (Bitmap.smoothing), and lets them move by less than a whole pixel (pixelSnapping auto, where the client pins always). | game:act |
uncacheRoom() | Keeps the room's art from being drawn out of its cache (cacheAsBitmap off under the room layer), so the scaled room and the name bars are drawn afresh at the size shown rather than stretched soft. | game:act |
render.gpu: Shaders on the graphics card, and what the card can do.
| Member | What it does | Plugins |
|---|---|---|
clear() | Ends the pass this area has on the card, if any, showing the game again. | game:act |
report() | What Flash Player can draw with here. | game:act |
shade() | Runs a shader of your own over the game on the card, and resolves with the pass once the loader has it, whatever state it is in. | game:act |
Methods
bloom
bloom(bloom?: BloomOptions, options?: FilterOptions) => Promise<RenderHandle>A cheap bloom: a GlowFilter, a blur of the shapes' own outline in one colour. quality 3 is about six times the work of 1; a radius twice as large is four times the area.
Parameters
| Name | Type | |
|---|---|---|
bloom optional | BloomOptions | |
options optional | FilterOptions |
Returns
Promise<RenderHandle>
Types
clear
clear() => Promise<void>Takes off everything this area put on: every filter, every hold, and the shader on the card, all back as the client had them. Safe to call when nothing is on.
Returns
Promise<void>
current
current() => Promise<RenderState>How the game is drawn now: quality, frame rate, scale mode and size, read in one message.
Returns
Promise<RenderState>
Types
filters
filters(specs: readonly FilterSpec[], options?: FilterOptions) => Promise<RenderHandle>Puts any of Flash's own filters on, in order, and takes them off together: a blur, a drop shadow, a chain of several. Each is another pass over the pixels every frame.
Parameters
| Name | Type | |
|---|---|---|
specs | readonly FilterSpec[] | |
options optional | FilterOptions |
Returns
Promise<RenderHandle>
Types
frameRate
frameRate() => Promise<number | null>stage.frameRate now: 24 is the client's own.
Returns
Promise<number \| null>
grade
grade(grade: GradeOptions, options?: FilterOptions) => Promise<RenderHandle>Grades the colours (a ColorMatrixFilter) over the room or the whole game, put back by the loader whenever the client replaces the filters. A filter rasterises what it is on before it is scaled, so over the room it costs a little sharpness as well as time.
Parameters
| Name | Type | |
|---|---|---|
grade | GradeOptions | |
options optional | FilterOptions |
Returns
Promise<RenderHandle>
Types
GradeOptions · FilterOptions · RenderHandle
Example
const plain = await phantom.aj.render.grade({ saturation: 1.2, contrast: 0.1 });
await plain();keepQuality
keepQuality(quality: "low" | "medium" | "high" | "best", options?: ActOptions) => Promise<RenderHold>Holds stage.quality against the client's own writes: the loader looks every twelfth frame and puts it back. The handle gives the client its own quality again. best is the one worth asking for, and the only one at which smoothAnimals shows on something moving; it costs processor time every frame.
Parameters
| Name | Type | |
|---|---|---|
quality | "low" | "medium" | "high" | "best" | |
options optional | ActOptions |
Returns
Promise<RenderHold>
Types
quality
quality() => Promise<"low" | "medium" | "high" | "best" | null>stage.quality now, or null before the game is up.
Returns
Promise<"low" \| "medium" \| "high" \| "best" \| null>
scaleMode
scaleMode() => Promise<"none" | "fill" | "fit" | "stretch" | null>How the game fills the window now (stage.scaleMode), or null for one with no name here.
Returns
Promise<"none" \| "fill" \| "fit" \| "stretch" \| null>
setFrameRate
setFrameRate(framesPerSecond: number, options?: ActOptions) => Promise<number>Sets the stage's frame rate, kept between 1 and 120, and resolves with the rate it now runs at. The client counts time in frames, so this makes the whole game run faster or slower, walking included: mostly for slowing it down to watch something.
Parameters
| Name | Type | |
|---|---|---|
framesPerSecond | number | |
options optional | ActOptions |
Returns
Promise<number>
Types
Example
const before = await phantom.aj.render.frameRate();
await phantom.aj.render.setFrameRate(12);
await phantom.aj.render.setFrameRate(before ?? 24);setQuality
setQuality(quality: "low" | "medium" | "high" | "best", options?: ActOptions) => Promise<"low" | "medium" | "high" | "best">Sets stage.quality once, and resolves with it once the stage reads it back. The client writes its own again on a window resize, a minigame and the world starting: for a quality that lasts, keepQuality.
Parameters
| Name | Type | |
|---|---|---|
quality | "low" | "medium" | "high" | "best" | |
options optional | ActOptions |
Returns
Promise<"low" \| "medium" \| "high" \| "best">
Types
setScaleMode
setScaleMode(mode: "none" | "fill" | "fit" | "stretch", options?: ActOptions) => Promise<"none" | "fill" | "fit" | "stretch">Sets how the game fills the window: fit (the client's own) shows all of it keeping its shape, stretch fills the window stretched, fill fills it keeping the shape and cuts off the rest, none does not scale. Lasts until set again or the game reloads.
Parameters
| Name | Type | |
|---|---|---|
mode | "none" | "fill" | "fit" | "stretch" | |
options optional | ActOptions |
Returns
Promise<"none" \| "fill" \| "fit" \| "stretch">
Types
shade
shade(shader: ShadeOptions, options?: FilterOptions) => Promise<RenderHandle>Runs a Pixel Bender kernel over the game (ShaderFilter), on the processor, every pixel every frame. Bytes that are no kernel are REFUSED by the player. For the graphics card, gpu.shade.
Parameters
| Name | Type | |
|---|---|---|
shader | ShadeOptions | |
options optional | FilterOptions |
Returns
Promise<RenderHandle>
Types
sharpen
sharpen(sharpen?: SharpenOptions, options?: FilterOptions) => Promise<RenderHandle>Sharpens with a three-by-three convolution. Past about amount: 1 the outlines ring; keepQuality('best') with uncacheRoom() is almost always the sharper picture, for free.
Parameters
| Name | Type | |
|---|---|---|
sharpen optional | SharpenOptions | |
options optional | FilterOptions |
Returns
Promise<RenderHandle>
Types
smoothAnimals
smoothAnimals(options?: ActOptions) => Promise<RenderHold>Smooths the animals, pets and anything else drawn as a bitmap (Bitmap.smoothing), and lets them move by less than a whole pixel (pixelSnapping auto, where the client pins always). New animals are smoothed as they walk in; the handle puts every one back as it was found. Shows only with keepQuality('best'): Flash ignores smoothing at low and medium.
Parameters
| Name | Type | |
|---|---|---|
options optional | ActOptions |
Returns
Promise<RenderHold>
Types
uncacheRoom
uncacheRoom(options?: ActOptions) => Promise<RenderHold>Keeps the room's art from being drawn out of its cache (cacheAsBitmap off under the room layer), so the scaled room and the name bars are drawn afresh at the size shown rather than stretched soft. It costs processor time, which is what the cache was buying.
Parameters
| Name | Type | |
|---|---|---|
options optional | ActOptions |
Returns
Promise<RenderHold>
Types
render.gpu
Shaders on the graphics card, and what the card can do.
clear
clear() => Promise<void>Ends the pass this area has on the card, if any, showing the game again.
Returns
Promise<void>
report
report(options?: GpuReportOptions) => Promise<GpuReport>What Flash Player can draw with here. Read driverInfo after a probe, not wmodeGPU: only asking the player for a context (and giving it back at once) settles it. A probe from another tab is refused (3688) unless waitForFrontMs gives someone time to bring the game to the front.
Parameters
| Name | Type | |
|---|---|---|
options optional | GpuReportOptions |
Returns
Promise<GpuReport>
Types
shade
shade(options: GpuShadeOptions) => Promise<GpuPass>Runs a shader of your own over the game on the card, and resolves with the pass once the loader has it, whatever state it is in. AGAL that does not assemble is REFUSED with the line it is on. One pass at a time: a second replaces the first, since several effects belong in one fragment program. Put the native filters on when the pass is anything but drawing.
Parameters
| Name | Type | |
|---|---|---|
options | GpuShadeOptions |
Returns
Promise<GpuPass>
Types
GpuShadeOptions · GpuPass · GpuPassState · ShaderPassState · ShaderPassAnswer
Types
The types the calls above take and give, with every field. Import any of them in a plugin from @phantomclient/plugin/sdk, or use them as they are in a script.
RenderState
How the game is drawn now, read from the stage.
| Field | Type | |
|---|---|---|
quality | "low" | "medium" | "high" | "best" | null | stage.quality, or null before the game is up. The client's own is medium. |
frameRate | number | null | stage.frameRate: the client's own is 24, and it counts time in frames. |
scaleMode | "none" | "fill" | "fit" | "stretch" | null | stage.scaleMode, by this area's names, or null for one it has none for. |
width | number | null | The stage's size in pixels. |
height | number | null |
ActOptions
How long something that acts may take, and what may end it first.
| Field | Type | |
|---|---|---|
timeoutMs optional | number | undefined | 10 seconds by default. |
signal optional | AbortSignal | undefined |
RenderHold
A hold the loader keeps on the game, walking its display tree; call it to put it all back.
type RenderHold = RenderHandle & {
/** How many display objects the first walk changed something on. */
readonly held: number;
}RenderHandle
Something this area put on the game: call it to take it off. Calling it twice does nothing.
type RenderHandle = () => Promise<void>GradeOptions
A colour grade: the same adjustments phantom.flash.recolor takes.
| Field | Type | |
|---|---|---|
brightness optional | number | undefined | -1 (black) to 1 (white); 0 leaves it. |
contrast optional | number | undefined | -1 to 1; 0 leaves it. |
saturation optional | number | undefined | 0 is grey, 1 leaves it, above 1 is more colourful. |
hue optional | number | undefined | Degrees around the colour wheel. |
matrix optional | number[] | undefined | A ColorMatrixFilter's own twenty numbers, instead of the adjustments above. |
FilterOptions
Where a filter goes, and how long it may take to go on.
type FilterOptions = ActOptions & {
/** Over the room alone (the default), or the whole game. */
where?: RenderWhere;
}SharpenOptions
| Field | Type | |
|---|---|---|
amount optional | number | undefined | How hard, 0 to 2: 0.5 by default. Past about 1 Animal Jam's thick outlines ring as halos. |
BloomOptions
| Field | Type | |
|---|---|---|
radius optional | number | undefined | How far the light spreads, in pixels: 8 by default. |
strength optional | number | undefined | How bright: 1 by default. |
color optional | number | undefined | The light's colour: white by default. |
quality optional | number | undefined | 1 to 3; each step is another pair of blur passes, by far the dearest number here. |
ShadeOptions
A Pixel Bender kernel (runs on the processor).
| Field | Type | |
|---|---|---|
bytes | string | The compiled .pbj bytecode, base64. There is no compiler for it any more. |
parameters optional | Record<string, number[]> | undefined | The kernel's parameters by name, each a list of numbers. |
precision optional | "full" | "fast" | undefined |
FilterSpec
One of Flash Player's own filters, named by the class it builds and the arguments that class takes, so what crosses says plainly what the loader will construct.
glow and matrix on a HighlightEffect came first and stay as they are: one filter, blended by a pulse. These are a list, put on in order and left at full strength, for the several at once a look is made of (a sharpen, then a grade, then a bloom).
type FilterSpec = | { kind: 'colorMatrix'; matrix: number[] }
/** `BlurFilter(blurX, blurY, quality)`. Quality is 1 to 3, each a further box pass each way. */
| { kind: 'blur'; blurX: number; blurY: number; quality: number }
/** `GlowFilter(color, alpha, blurX, blurY, strength, quality, inner, knockout)`. */
| {
kind: 'glow';
color: number;
alpha: number;
blurX: number;
blurY: number;
strength: number;
quality: number;
inner: boolean;
knockout: boolean;
}
/** `DropShadowFilter(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject)`. */
| {
kind: 'dropShadow';
distance: number;
angle: number;
color: number;
alpha: number;
blurX: number;
blurY: number;
strength: number;
quality: number;
inner: boolean;
knockout: boolean;
hideObject: boolean;
}
/**
* `ConvolutionFilter(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha)`:
* a weight for each pixel around the one being drawn, which is what a sharpen is.
*/
| {
kind: 'convolution';
matrixX: number;
matrixY: number;
matrix: number[];
divisor: number;
bias: number;
preserveAlpha: boolean;
clamp: boolean;
color: number;
alpha: number;
}
/**
* `ShaderFilter(new Shader(bytes))`: a Pixel Bender kernel, its bytecode base64 as `bytes`, which
* the lo…GpuReportOptions
| Field | Type | |
|---|---|---|
probe optional | boolean | undefined | Ask the player for a context to find out, and give it back: the only real answer. |
timeoutMs optional | number | undefined | How long to wait for the context to arrive: 5 seconds. |
waitForFrontMs optional | number | undefined | How long to keep asking while the game is in the background, for someone to bring it front. |
GpuReport
What Flash Player can draw with on this computer.
| Field | Type | |
|---|---|---|
wmodeGPU | boolean | Stage.wmodeGPU: the display list composited on a card. False under direct, so no answer. |
stage3Ds | number | How many Stage3D layers the player offers: 4 wherever it offers any. |
driverInfo | string | null | Context3D.driverInfo, the renderer's own name, or null where nothing asked for a context. |
hardware | boolean | null | Whether driverInfo names a real card; null where there is no name to read. |
probed | "refused" | "not-asked" | "already" | "made" | "timed-out" | What the probe did: not asked, a context already there, made, refused, or timed out. |
error | string | null | The player's own words for a refusal. |
says | string | null | The refusal in plain words, with what to do about it, where there are words for it. |
embed | string | null | How the movie is embedded in its page (direct is Stage3D's), from the page Phantom wrote. |
pageRenderer | string | null | What the game's page draws with (WebGL renderer), none, or null where it will not say. |
drawing | boolean | null | Whether the game was being drawn while this asked, measured; null without a probe. |
GpuShadeOptions
A shader on the graphics card: AGAL source, assembled here.
type GpuShadeOptions = ActOptions & {
/**
* The fragment program, in AGAL. It is handed the captured frame on sampler `fs0` with the
* texture coordinate in `v0`, and writes the pixel to `oc`:
* `tex ft0, v0, fs0 <2d, clamp, linear, nomip>` then `mov oc, ft0` changes nothing.
*/
fragment: string;
/** The vertex program: Phantom's fullscreen one by default, which there is little reason to change. */
vertex?: string;
/** Fragment constants from `fc0` up, four numbers a register, written as the pass starts. */
constants?: number[][];
/**
* The register written every frame with `[1 / width, 1 / height, seconds, frames]`: the one
* after `constants` by default, `null` for none.
*/
frameRegister?: number | null;
/** Capture and draw every this many frames: 1 by default. */
everyFrames?: number;
/** How often to ask for a context again while the player refuses one, in frames: 48. */
retryFrames?: number;
/** Draw on a software renderer too. Off, and worth leaving off: the filters are cheaper. */
allowSoftware?: boolean;
/** How often to be told the costs while drawing, in frames: 120. 0 for changes alone. */
reportFrames?: number;
}GpuPass
A shader pass on the card: call it to end the pass and show the game again. state is where it got to; onState hears it change. It is normal to start waiting (the player gives no context to a game in the background), and nothing of the game is hidden unless it is drawing.
type GpuPass = (() => Promise<void>) & {
readonly state: GpuPassState;
onState(listener: (state: GpuPassState) => void): () => void;
}GpuPassState
What a GPU pass is doing and costing, with the reason in words where there are words for it.
type GpuPassState = ShaderPassAnswer & { says: string | null }ShaderPassState
What a shader pass is doing: whether it is drawing, and if not, why not.
type ShaderPassState = | 'waiting'
/** A context came back, and it is the software renderer, which `allowSoftware` was not set for. */
| 'software'
/** The programs would not upload, or the buffers would not be made. It will not try again. */
| 'failed'
/** Running: the game is being captured and drawn through the shader on the card. */
| 'drawing'ShaderPassAnswer
What a shader pass is doing, and what it costs. Answered by shader and by shaderStats.
| Field | Type | |
|---|---|---|
state | ShaderPassState | |
driverInfo | string | null | Context3D.driverInfo, the renderer's own name, once there is a context to ask. |
hardware | boolean | null | Whether driverInfo names a real card. Null until there is one. |
error | string | null | Whatever went wrong last, in the player's own words: a refusal, or a program that would not upload. |
width | number | The back buffer and the texture, in pixels: the game pane's own size. |
height | number | |
frames | number | Frames drawn since the pass started. 0 while it has never drawn. |
captureMs | number | What each step of a frame took, in milliseconds: the capture (BitmapData.draw), the upload into the texture, and the draw and present. Smoothed over roughly the last sixteen frames, so one slow frame does not swamp them; the worst single frame is kept separately and unsmoothed, because that is the one that drops a frame. Measured rather than guessed, which is the only way to say whether a pass pays: add them up and compare with budgetMs. |
uploadMs | number | |
drawMs | number | |
worstMs | number | The worst single frame of the three steps added together, since the pass started. |
budgetMs | number | How long a frame has: 1000 divided by the stage's frame rate, which the client sets to 24. |
bytesPerFrame | number | How many bytes the capture uploads a frame: four a pixel. |
hidden | boolean | Whether the game is hidden now, which it is only while the pass is actually drawing. |