3 IPC Input
Tearnote edited this page 2026-09-02 12:36:04 +01:00

Playnote > IPC input

Playnote accepts synthetic input via an IPC interface transporting JSON objects. These inputs can control only the UI. This is useful for automated testing and may in the future be expanded with higher-level commands, such as letting Twitch chat control song selection.

Caution

IPC input cannot control gameplay.

On Linux, IPC uses an abstract socket named @playnote-ipc-<uid>. On Windows, it uses a named pipe at \\.\pipe\dev.playnote.Playnote-<sid>, where <sid> is the current Windows user's security identifier.

Lifecycle

A client connection begins with an information query:

{"type": "ipc.info"}

The server replies with the current instance kind:

  • graphical: the normal game instance
  • headless: a running command-line process
{"type": "ipc.info", "kind": "graphical"}

Naturally, only a graphical instance will accept synthetic inputs.

All future messages receive an acknowledgement. This verifies only that the message was received and is valid JSON matching the expected schema. The command may still fail if its values are unexpected; semantic failures are logged only by the running instance.

Note

An ipc.accepted response does not mean that the requested action succeeded.

{"type": "ipc.accepted"}
{"type": "ipc.rejected", "reason": "..."}

Input syntax

Button actions use the following messages:

{"type": "input", "version": 1, "action": "accept"}
{"type": "input", "version": 1, "action": "cancel"}
{"type": "input", "version": 1, "action": "start"}
{"type": "input", "version": 1, "action": "select"}

Navigation additionally accepts:

  • coarse: Optional boolean, defaulting to false. Requests larger semantic steps, such as moving by a word or page.
  • index: Optional 0 or 1, defaulting to 0. Identifies the P1 or P2 side when relevant.
{"type": "input", "version": 1, "action": "next"}
{"type": "input", "version": 1, "action": "previous", "coarse": true, "index": 1}

Adjustment additionally accepts:

  • precision: Optional boolean, defaulting to false. Requests a finer adjustment step.
  • index: Same as above.
{"type": "input", "version": 1, "action": "increase"}
{"type": "input", "version": 1, "action": "decrease", "precision": true, "index": 1}

Example

A Python client for Linux:

import json
import os
import socket

endpoint = "\0playnote-ipc-{}".format(os.getuid())
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as client:
    client.connect(endpoint)

    client.sendall(json.dumps({"type": "ipc.info"}).encode())
    info = json.loads(client.recv(64 * 1024))
    if info.get("kind") != "graphical":
        raise RuntimeError("Playnote is not running as a graphical instance")

    client.sendall(json.dumps({
        "type": "input",
        "version": 1,
        "action": "accept",
    }).encode())
    acknowledgement = json.loads(client.recv(64 * 1024))
    if acknowledgement.get("type") != "ipc.accepted":
        raise RuntimeError(acknowledgement)