OBSERVABILITY · ROOM VISUAL DEBUGGING

How to Draw RoomVisual Debug Shapes Without a Fill

CURATED PATH

These links are maintained for a concrete prerequisite, failure mode, debugging step, or next lesson instead of generic topic similarity.

Debugging / Observability

Recommended next steps

Verify the previous tick with Event LogTime evidenceContain a Room that throws exceptionsException handlingDebug a Creep that is not movingMovement branchDiagnosticsContinue diagnosis
Verification statusOfficial API: Checked — RoomVisual browser output, one-tick lifetime, room coordinates, chaining, clear(), getSize(), export() and import() · Size boundary: Checked — one Room may contain at most 512,000 bytes of serialized visual data in the current tick · Evidence boundary: Drawing output is kept separate from movement, combat, harvesting and other game-action results

VERIFICATION

Evidence and test status

Official API
Checked — RoomVisual browser output, one-tick lifetime, room coordinates, chaining, clear(), getSize(), export() and import()
Size boundary
Checked — one Room may contain at most 512,000 bytes of serialized visual data in the current tick
Evidence boundary
Drawing output is kept separate from movement, combat, harvesting and other game-action results
JavaScript syntax
Checked for the focused examples in this guide
Screeps Console test
Pending
Live browser rendering test
Pending
Last verified
August 30, 2026

Quick answer

Need an outline with no interior fill? For circle() and rect(), use fill: 'transparent'. The official API documents fill as a string; null is not the documented no-fill value.

Use RoomVisual to draw temporary debug information directly over a Room: circles around Creeps, text labels for state, and lines toward targets. The drawings are visible only to you and last for one tick unless your code draws them again.

For a visible Room, room.visual is the simplest entry point. Keep every position in the same Room as the visual, and remember that a drawing only describes what your debug code chose to render. It does not prove that a movement, harvest, build, or combat action succeeded.

Draw one label and circle

Start with one visible Creep. This example does not change game state; it only adds current-tick browser visuals.

const creep = Game.creeps.Worker1;

if (creep) {
  creep.room.visual
    .circle(creep.pos, {
      radius: 0.45,
      fill: 'transparent',
      stroke: '#ffffff'
    })
    .text(
      creep.memory.role ?? creep.name,
      creep.pos.x,
      creep.pos.y - 0.7,
      { font: 0.4 }
    );
}

Every drawing method returns the same RoomVisual object, so chaining calls is supported by the API.

Draw a Creep and its target

A useful debug layer often answers three questions at a glance: where is the Creep, what state is it in, and which target is it trying to use?

function drawCreepTarget(creep, target, label) {
  if (!creep?.pos) return 'creep-missing';

  const visual = creep.room.visual;
  visual.circle(creep.pos, {
    radius: 0.4,
    fill: 'transparent',
    stroke: '#ffffff'
  });
  visual.text(
    String(label).slice(0, 60),
    creep.pos.x,
    creep.pos.y - 0.7,
    { font: 0.4 }
  );

  if (!target?.pos) {
    return 'target-missing';
  }

  if (target.pos.roomName !== creep.pos.roomName) {
    return 'target-in-another-room';
  }

  visual.line(creep.pos, target.pos, {
    lineStyle: 'dashed',
    opacity: 0.5
  });
  visual.circle(target.pos, {
    radius: 0.35,
    fill: 'transparent',
    stroke: '#ffffff'
  });

  return 'visual-added';
}

The explicit Room check prevents numeric coordinates such as 25,25 in one Room from being drawn as though they referred to the same tile in another Room.

Keep coordinates in the correct Room

room.visual belongs to that Room. A RoomPosition also contains a roomName, so compare Room identity before drawing a target path or copying coordinates.

You can also create new RoomVisual('W1N1') for a Room that is not currently visible to your script. That lets you post known coordinates, but it does not grant visibility and does not make Game.rooms.W1N1 or unseen game objects available.

Redraw visuals every tick

Room visuals are not stored as persistent game state. They last one tick and disappear if they are not updated.

That makes them useful for current decisions such as:

  • the target selected this tick;
  • the current role or task label;
  • a path or anchor your code is considering;
  • an immediate API return code you already captured.

If a marker seems to flicker or vanish, first check whether the code that draws it actually ran on the current tick. Do not solve one-tick lifetime by storing large visual payloads in Memory unless replay is a real requirement.

Watch the Room visual size

visual.getSize() returns the total serialized size of visuals already added to that Room in the current tick. The official per-Room limit is 512,000 bytes.

function drawDebugLabel(room, pos, text) {
  if (!room || !pos || pos.roomName !== room.name) {
    return 'invalid-room-or-position';
  }

  const visual = room.visual;
  const softLimit = 480000;

  if (visual.getSize() >= softLimit) {
    return 'soft-visual-limit-reached';
  }

  visual.text(
    String(text).slice(0, 80),
    pos.x,
    pos.y,
    { font: 0.4 }
  );

  return visual.getSize() <= 512000
    ? 'visual-added'
    : 'hard-limit-crossed';
}

The 480,000-byte value is a project safety margin, not an official threshold. Because the next serialized drawing has a nonzero size, checking a soft margin before adding more debug labels is safer than intentionally filling the full 512,000-byte limit.

Use clear, export, and import deliberately

clear() removes all visual data already added to that Room in the current tick. If several modules draw to the same Room, a late clear() can remove another module's diagnostics too.

export() returns a compact string containing the current Room visual data. import() adds previously exported visual data to the current tick. These methods are useful for deliberate replay or composition, but most debugging code only needs direct drawing calls each tick.

const room = Game.rooms.W1N1;

if (room) {
  const snapshot = room.visual.export();

  if (typeof snapshot === 'string') {
    Memory.lastRoomVisual = snapshot;
  }
}

// On a later tick, when deliberate replay is useful:
const currentRoom = Game.rooms.W1N1;
if (currentRoom && typeof Memory.lastRoomVisual === 'string') {
  currentRoom.visual.import(Memory.lastRoomVisual);
}

Keep replay payloads bounded and temporary. Exporting a visual does not turn the underlying game objects or actions into historical evidence.

Visuals are not action evidence

A RoomVisual method returning its RoomVisual object means the drawing call was chained successfully. It does not report the success of a separate Screeps action.

For example, if creep.moveTo(target) returned OK, you may draw that numeric result beside the Creep. The label still only records what your code knew during that tick. Verify movement from the Creep's position on a later tick; use the moveTo() diagnostic when the accepted movement does not produce position progress.

For event-based follow-up around repairs, attacks, and similar Room activity, use the Room.getEventLog() guide. The visual layer and the event log answer different questions.

Debugging checklist

  • Confirm the drawing code ran on the current tick.
  • Use room.visual for a currently visible Room.
  • Check roomName before connecting two positions.
  • Remember that visuals disappear after one tick unless redrawn.
  • Use getSize() when a large diagnostic layer may approach the 512,000-byte Room limit.
  • Use clear() carefully when several modules write visuals.
  • Keep export/import as an optional replay tool, not the default debug architecture.
  • Preserve actual action return codes separately.
  • Verify game-state results from later objects, Stores, positions, or event data rather than from the drawing itself.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide keeps the technical scope and verification boundaries of its Chinese source. The verification record shows which checks were completed and which runtime observations are still pending.