# Drawings

> Draw the pitch, the players and the ball with shapes that follow discs, and choose who sees each one.

The world's discs and walls have no look of their own. What players see is
what your game **draws**: shapes that sit on the field or follow a disc.

## Draw the pitch

`world.draw` makes a shape. Shapes belong to the match, so draw them when it
starts:

```ts title="src/pitch.ts"
// The pitch: grass, a halfway line and a center circle. Drawn once, at the
// start of the match.
game.on("start", () => {
  world.draw.box({ width: 600, height: 300, fill: GRASS });
  world.draw.line({
    points: [
      { x: 0, y: -150 },
      { x: 0, y: 150 },
    ],
    stroke: LINE,
    strokeWidth: 2,
  });
  world.draw.circle({ radius: 50, stroke: LINE, strokeWidth: 2 });
});
```

| Shape     | Draws                                    |
| --------- | ---------------------------------------- |
| `circle`  | a circle, or a disc's body when anchored |
| `box`     | a rectangle, with rounded corners        |
| `polygon` | a closed outline through its points      |
| `line`    | a line through its points, with arrows   |
| `arc`     | part of a circle's outline               |
| `text`    | a label that stays upright               |
| `image`   | an image from your package               |
| `group`   | the shapes inside it, moved together     |

Every shape takes a `position`, `rotation`, `scale`, `fill`, `stroke`,
`strokeWidth`, `dash` and `opacity`. A shape without `fill` is hollow; one
without `stroke` has no outline.

## Follow a disc

With an `anchor`, a shape moves with a disc. A circle without a `radius`
takes the disc's. A `group` keeps shapes together and in order: later shapes
are drawn on top.

```ts title="src/players.ts"
// Players above the pitch: one group, so they are always drawn on top.
let players: Disko.Group | null = null;

game.on("start", () => {
  players = world.draw.group();
});

// Each player's disc gets a body, a ring only they see, and a name.
game.on("player-enter", ({ player }) => {
  const body = world.createDisc({ radius: 15 });
  const color = hsl(player.id * 47, 70, 55);

  player.control(body);
  players?.add(
    world.draw.circle({
      anchor: body,
      radius: 19,
      stroke: withAlpha(color, 0.5),
      strokeWidth: 2,
      audience: { include: [player] },
    }),
    world.draw.circle({
      anchor: body,
      fill: color,
      stroke: darken(color, 0.3),
      strokeWidth: 2,
    }),
    world.draw.text({
      anchor: body,
      position: { x: 0, y: -28 },
      text: player.name,
      size: 13,
      sizing: "screen",
      fill: rgb(255, 255, 255),
    }),
  );
});
```

Each shape can have an [audience](/games/audiences): the ring above reaches
only its own player.

When a disc is destroyed, the shapes that follow it go too.

## Colours

`rgb`, `rgba`, `hsl` and `hsla` make colours. `disko:color` mixes them:
`mix`, `lighten`, `darken` and `withAlpha`. The same colours work in chat
and in Menu and HUD styles.

## Move every frame

Any number can be an [expression](/games/cameras-and-expressions) that every
screen works out on each frame, so a shape can spin, pulse or fade with no
code in your tick handler:

```ts title="src/ball.ts"
// The ball spins as it rolls: its turn is an expression every screen works
// out on each frame.
game.on("start", () => {
  const ball = world.createDisc({ radius: 10, damping: 0.99 });

  world.draw.group({
    anchor: ball,
    rotation: expr.times(expr.time, 4),
    children: [
      world.draw.circle({ anchor: ball, fill: rgb(255, 255, 255) }),
      world.draw.line({
        points: [
          { x: -10, y: 0 },
          { x: 10, y: 0 },
        ],
        stroke: rgb(30, 30, 30),
      }),
    ],
  });
});
```

## Change and remove

```ts title="src/banner.ts"
// A drawing changes with `update`, and goes with `destroy`.
let banner: Disko.Drawing | null = null;

game.on("start", () => {
  banner = world.draw.text({
    position: { x: 0, y: -170 },
    text: "Kickoff",
    size: 24,
    fill: rgb(255, 214, 102),
  });
});

game.on("tick", ({ tick }) => {
  if (tick === 180) {
    banner?.update({ text: "Play!" });
  } else if (tick === 300) {
    banner?.destroy();
    banner = null;
  }
});
```

## See the physics

Test rooms and the Studio can show the **physics view** over your drawings:
every disc, wall, crossing and joint as disko moves them. Use it to check
that what players see matches what collides.

See the [`world` reference](/reference/world#drawing) for every shape and
option.
