Skip to content

Runtime Context ​

ctx is the Tissue Preview Runtime context passed into script lifecycle methods.

It is the entry point for scripts to access the current GameObject, project data, shared state, and supported runtime capabilities.

js
export default {
  start(ctx) {
    ctx.transform.setPosition(100, 200)
  },
  update(ctx, dt) {
    // dt is seconds
  }
}

ctx is not Phaser ​

ctx is not a raw Phaser object and is not a Phaser Scene.

Do not guess APIs from Phaser, Cocos, Godot, or other engines. Only APIs listed in the Runtime Manifest are supported.

Unsupported examples:

js
ctx.now()
ctx.time
ctx.clock
ctx.scene.time
ctx.gameObject.list
ctx.parentContainer
ctx.physics

If a capability is missing, use the supported Tissue API or report the missing capability.

Lifecycle ​

Recommended script shape:

js
export default {
  awake(ctx) {},
  start(ctx) {},
  update(ctx, dt) {},
  onPointerDown(ctx) {},
  onDestroy(ctx) {}
}

awake(ctx) ​

Called when the script instance is created. Use it for local fields.

js
awake(ctx) {
  this.vy = 0
  this.elapsed = 0
}

start(ctx) ​

Called once when the script starts. Use it for input registration and shared state initialization.

js
start(ctx) {
  ctx.state.score = 0
  ctx.input.onKeyDown('SPACE', () => {
    this.vy = -320
  })
}

update(ctx, dt) ​

Called every frame. dt is already in seconds.

js
update(ctx, dt) {
  this.elapsed += dt
  ctx.transform.setPosition(ctx.self.x, ctx.self.y + 100 * dt)
}

onPointerDown(ctx) ​

Called when the current object is clicked.

js
onPointerDown(ctx) {
  ctx.state.started = true
  ctx.node.hide()
}

Core rules ​

Use only APIs listed in the manifest ​

Wrong:

js
const t = ctx.now()

Correct:

js
this.elapsed += dt

dt is seconds ​

Wrong:

js
this.y += speed * (dt / 1000)

Correct:

js
this.y += speed * dt

Use ctx.state for shared state ​

js
// StartButton.js
onPointerDown(ctx) {
  ctx.state.started = true
  ctx.node.hide()
}
js
// Bird.js
update(ctx, dt) {
  if (!ctx.state.started) return
}

Use this for local script state ​

js
awake(ctx) {
  this.elapsed = 0
}

update(ctx, dt) {
  this.elapsed += dt
}

Common properties ​

ctx.self ​

Current editor GameObject data.

js
const startX = ctx.self.x
const width = ctx.self.width

ctx.gameObject ​

Current Phaser GameObject.

Avoid relying on Phaser internals. Prefabs in Tissue are GameObject hierarchies, not Phaser Containers.

Do not use:

js
ctx.gameObject.list
ctx.gameObject.parentContainer

ctx.project ​

Current project data.

js
const width = ctx.project.width
const height = ctx.project.height

ctx.state ​

Shared state for the current preview scene.

js
start(ctx) {
  if (typeof ctx.state.score !== 'number') {
    ctx.state.score = 0
  }
}

Alternatives to unsupported APIs ​

UnsupportedUse instead
ctx.now()this.elapsed += dt
ctx.timeAccumulate time in update(ctx, dt)
ctx.scene.timeFuture Tissue ctx.time wrapper if needed
ctx.gameObject.listinstance.objects or instance.find(name) from ctx.instantiatePrefab()
ctx.parentContainerTissue GameObject hierarchy or prefab instance handle
ctx.physicsctx.transform + ctx.collider until a Physics component exists

AI rules ​

AI-generated scripts must:

  • Treat ctx as Tissue Runtime Context, not Phaser.
  • Never guess APIs.
  • Use only APIs listed in the Runtime Manifest.
  • Treat dt as seconds.
  • Use this.elapsed += dt for timers.
  • Use ctx.state for shared state.
  • Use the prefab instance handle returned by ctx.instantiatePrefab().
  • Report missing capabilities instead of silently falling back.

Manifest ​