Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/zshall/program-guide/llms.txt

Use this file to discover all available pages before exploring further.

The channel system in Television Simulator ‘99 is built for extension. Every watchable channel is a JavaScript class that inherits from the shared Channel base class, paired with an HTML layout file that gets loaded on demand into the screen div. Adding your own channel means creating a directory, writing a layout and a class, then registering the class in two places — index.html and tv.js.
1

Create the channel directory

Channel directories live under channels/ and must be named with the channel number zero-padded to three digits:
mkdir channels/005
The directory must always use a zero-padded three-digit name regardless of the actual channel number. Channel 5 → channels/005/, channel 42 → channels/042/. This is enforced by Helpers.padLeft(number, 3) inside tv.js’s showChannel() method.
2

Create layout.html

layout.html is the HTML fragment loaded into the .current-channel div via jQuery’s .load() call when a viewer switches to your channel. It should contain every DOM element your channel class will manipulate: a YouTube player target, info panels, or any other layout you want to render inside the TV screen.
<div id="ytplayer" class="video-right"></div>
<div class="video-left">
  <div class="inner-box"></div>
</div>
<div class="guide-container">
  <table class="guide-table" cellpadding="0" cellspacing="0">
    <thead>
      <tr>
        <th class="realtime"></th>
        <th class="time time-plus00"></th>
        <th class="time time-plus30"></th>
        <th class="time time-plus60"></th>
      </tr>
    </thead>
    <tbody id="listing-grid"></tbody>
  </table>
</div>
The id="ytplayer" div is the mount point for the YouTube IFrame API. The .current-channel div also receives the id attribute ch-{number} (e.g. ch-5) automatically when the channel loads, so your SCSS can scope styles to it.
3

Create script.js — the channel class

Each channel is a class that extends Channel. The constructor chains to super(), show() is called once the layout HTML has been injected, and teardown() is called before any channel switch. Below is a complete working example for channel 5:
class Channel5 extends Channel {
    constructor(container, guideData) {
        super(container, guideData);
    }

    show() {
        super.show();
        this.player = new YT.Player('ytplayer', {
            height: '360',
            width: '425',
            videoId: 'YOUR_VIDEO_ID',
            events: {
                'onReady': (event) => event.target.playVideo()
            },
            playerVars: {
                'autoplay': 1,
                'controls': 0,
                'rel': 0
            }
        });
    }

    ready() {
        // Called by tv.js after the fade-in animation completes.
        // The base Channel class does NOT define ready(), so every
        // subclass must implement this method — even if it does nothing —
        // or the TV will throw a TypeError when it calls this.channel.ready().
    }

    teardown() {
        super.teardown();
    }
}
Replace 'YOUR_VIDEO_ID' with the 11-character YouTube video ID you want to play on this channel.
The class name must follow the pattern Channel{number}, matching the channel’s number attribute in guide.xml exactly. Channel number 5 → Channel5. The TV core constructs the class dynamically via new this.classes['Channel' + number](...), so a mismatch will throw a runtime error.
4

Load the script in index.html

Channel scripts must be loaded before js/core/tv.js so the class is defined when the TV initialises. Open index.html and add your script tag in the channel scripts block:
<!-- Load application JS -->
<script src="channels/005/script.js"></script>
<script src="js/core/tv.js"></script>
The existing channels/012/script.js line shows exactly where to add it.
5

Register the class in tv.js

Open js/core/tv.js and add your new class to the this.classes object inside the TV constructor:
this.classes = {
    Channel12,
    Channel5
};
This is the registry the TV uses to instantiate the correct class when showChannel() is called. Every channel class you write must appear here.
6

Add the channel to guide.xml

For your channel to appear in the listings grid and be switchable, add a <channel> element with the watchable attribute to data/guide.xml:
<channel number="5" name="MYCH5" watchable="watchable">
  <listing timeslot="33" type="1">Evening News</listing>
  <listing timeslot="35" type="movie" rating="TV-PG">Movie Title (1999)</listing>
</channel>
Omitting watchable means the channel row still appears in the guide table but the TV will not attempt to load it when the number is dialled. See Customizing Channel Listings for full details on the XML schema.

Channel lifecycle

When a viewer switches to your channel, the TV runs three methods in sequence:
MethodWhen it’s calledWhat to do here
show()Immediately after layout.html is injected into the DOMInitialise the YouTube player, set up DOM references, start intervals
ready()After the fade-in animation completes (warmupTime ms on first load, warmupTime / 10 on subsequent switches)Start any animations or interactions that should only begin once the screen is fully visible. Must be defined on every subclass — the base Channel class does not provide this method and tv.js always calls it.
teardown()Before loading a different channelStop timers, stop playback — handled automatically by super.teardown() if you use this.timeouts and this.intervals
The base Channel class does not define ready(). After the fade-in animation, tv.js unconditionally calls this.channel.ready(). If your subclass omits ready(), the browser will throw a TypeError: this.channel.ready is not a function the moment the screen finishes fading in. Always implement ready() — it can be empty if you have nothing to do there.
Always call super.show() at the start of show() and super.teardown() at the end of teardown(). The base teardown() in channel.js clears all registered timeouts and intervals, stops the YouTube player, and empties the container:
teardown() {
    for (var i in this.timeouts) {
        clearTimeout(this.timeouts[i]);
    }
    for (var i in this.intervals) {
        clearInterval(this.intervals[i]);
    }
    if (this.player) {
        YouTubeApi.stopVideo(this.player);
    }
    this.container.empty();
    this.container.attr('id', '');
}

Managing timers

Store every setTimeout and setInterval return value in this.timeouts or this.intervals respectively. The base teardown() iterates over both objects and clears everything automatically, so you never have to track timer IDs manually:
// In show():
this.intervals.clock = setInterval(() => {
    this.realtime.text(moment().format('h:mm:ss'));
}, 1000);

this.timeouts.intro = setTimeout(() => {
    this.showIntroText();
}, 2000);
Channel12 uses this pattern for its live clock: this.intervals.timeInterval = setInterval(...).

YouTube player and mute control

The TV’s hardware mute button (#tv-mute in index.html) calls YouTubeApi.toggleMuteVideo(this.channel.player). If this.player is not set on your channel instance, clicking mute will throw an error and the control will stop working. Always assign the YT.Player instance to this.player — not any other property name — in your show() method.

Build docs developers (and LLMs) love