ProTV by ArchiTechAnon | Usage Guides and Walkthroughs

[These posts are currently in WIP state and will be updated over time]

Asset Introduction and Overview

ProTV (current release version: 2.0) is a pay-what-you-want asset that intends to make working with video players a more enjoyable experience by enabling extensibility through event driven activity.

Table Of Contents:

What is ProTV exactly?

At its core, ProTV is a central management script that brokers the state and activity of video players that are connected to it. It is capable of handling multiple video players by swapping between them and retaining proper sync to other users while doing so. Based on the state and activity of the management script, it will propagate events and data to other udon scripts that have registered themselves as a listener.

Where can I get it?

The mainline stable releases are available as pay-what-you-want (including free) on:
Gumroad (
Booth (

Beta releases (containing the latest feature development) is available via discord (Join Here)

Monetary support for the project also accepted on Ko-Fi

How do I use it?

If you are looking for an out-of-the-box solution that doesn’t take much if any effort to add to your world, check out the Assets/ArchiTechAnon/ProTV/Prefabs folder after importing. This is a selection of TVs that should #justwork. They also support multiple independent copies[1] in-world, no problem. Though if you want something a bit more custom, please continue reading though the guides to get a full grasp of how the ProTV system is setup.

[1] Note: do be aware that in excess of 8 in a world, some performance loss may become apparent.

How does it work?

The primary driver behind this asset is the ``````````````TVManagerV2.cs script (aka “main script”). When the developer is setting up the world with a new TV, they will need to first declare what video players the main script will manage. This is done by attaching either a Unity Player or AVPro Video Player script into the scene and adding the VideoManagerV2.cs script to that same game object. Then drag the game object to the Video Players array of the TVManagerV2 script inspector. Repeat until the desired video players are connected. This is the fundamental setup.

With this completed, the main script will now broker state and activity between the connected video players and any udon behavior that declares itself a listener to the main script.

[TODO insert architecture flowchart]

How can I customize it for my needs?

There are quite a number of ways that you can customize ProTV for various situations. The two most meaningful ways to customize are related to the Speakers (audio sources[2]) / Screens (mesh w/ shader) as well as various Plugins that are either provided in the asset or that you can make yourself (more on plugins in a bit).

[2] If you are wanting to customize the audio sources a lot, I highly recommend you have a look at TEXTLOGO’s Audio Demo World for ideas and general setup for certain audio considerations and situations.

1 Like

Core Architecture

The architecture of ProTV has two main concepts.

  1. Centralized management of multiple video player components.
  2. Event Listener Registry for propagating information to other udon behaviors.

Centralized Management

In order to give developers freedom over what player configuration they wish to use, ProTV implements a proxy-ish script that sits on the same game object as the desired video player component (being either Unity player or AVPro player). This script is called VideoManagerV2.

Each video manager has a handful of configuration options available to it. It also has two lists that should be populated as needed: Speakers and Screens.
These lists associate any Audio Source and Mesh Renderer game objects to the script for automatic management.

These proxies are then also associated with the main script called TVManagerV2. The tv manager has a handful of config options as well for the overall operation of its particular instance of ProTV.

TV Options

  • Autoplay URL
    A url that is automatically loaded as soon as the TV has finished initializing. This is considered a “default” field because plugins are able to override this setting as needed (more on this later).

  • Autoplay URL Alt [2.1 BETA FEATURE]
    Part of the “Alternate URL” feature. Similar to the standard autoplay url, it will automatically load as soon as the TV has finished initializing.

  • Autoplay Start Offset
    Specifies how many seconds to wait after initialization (basically when the world is loaded in) to attempt to load the autoplay video. This is primarily used to avoid RateLimit errors or to specify load order when there are multiple TVs in the world that have some sort of autoplay defined on them. If there are one or less TVs with autoplay, this setting can be ignored.

  • Initial Volume
    Pretty straight forward. This specifies what the volume should be for the TV at load time between 0% and 100% (represented as a 0.0 to 1.0 decimal)

  • Initial Player
    This integer value specifies which index of the related Video Managers array option the TV should start off with. The array of video managers is 0-index based. This means that if you have, say, 3 video players in the list, if you wanted to have the TV default to the second video manger in that list, this option would need the value of 1 in it. If you only have one video manager, then set this option to 0.

  • Paused Resync Threshold
    This is more so a visual feedback feature where if the video is paused locally but the video owner is still playing, the video will update the current playback timestamp every some number of seconds determined by this option. If you don’t want the video to resync while paused, set this value to Infinity (yes the actual word) to have it never update until the user resumes the video.

  • Automatic Resync Interval
    Setting for telling the TV how many seconds to wait before trying to resync. This affects two separate things.

    • First is the Audio/Video sync. This particularly affects AVPro for long form videos (like movies) where after some time or excessive CPU hitching (such as repeated alt-tabbing) can cause the audio to eventually become out-of-sync with the video (usually it’s the video that falls behind).
    • Second is the Owner sync. This is where the owner of the current video determines the target timestamp that everyone should be at. ProTV has a loose owner sync mechanism where it will only try to enforce sync at critical points (like switching videos, the owner seeks to a new time in the video, owner pauses the video for everyone, etc).

    This option defines how many seconds to wait before attempting to do the above resync.

  • Retry Live Media
    This is for specifying how many retry attempts should be made when live media fails. Basically when a livestream goes down, ProTV will attempt to reload the same URL some number of times, determined by this setting, before determining that the stream should actually be considered failed/finished. This was originally created to help when doing things like swapping DJs on a VRCDN stream.

  • Start Hidden
    NOTE: This option is considered deprecated and shouldn't be used unless you know what you are doing. It is recommended to simply disable the TV gameobject in the inspector manually if you want it to start off as hidden.
    This setting automatically hides the TV’s gameobject after initialization, but before and autoplay video has been loaded. Useful if you have some plugins that need to modify the TV during the init phase.

  • Allow Master Control
    A security setting that declares whether or not the instance master (aka the user who has been in the instance the longest) has the right to lock/manage the TV.
    NOTE: The instance owner (aka the creator of the instance itself) will ALWAYS have the right to lock/manage the TV regardless of master.

  • Sync To Owner
    A flag to tell the TV whether or not it’s supposed to synchronize with the owner. The setting is typically set to true. Though, for example, it can be particularly useful when trying to make a local-only music player.

  • Video Managers
    The other part of the Initial Player setting. This is the actual list of VideoManager scripts that the TV should be handling.


Plugins Overview


MediaControls Plugin Deep Dive


Playlist Plugin Deep Dive


Queue Plugin Briefing


Skybox Plugin Briefing

This plugin encompasses a minimal plugin script that swaps the scene’s skybox material on certain TV events and the shader used to render various video modes of the skybox.
It supports equi-rectangular (aka panoramic) 360deg video and 180deg video as well as certain cubemap style 360 video renders.
It also supports Side-By-Side (SBS) and Over-Under (OU) 3D modes for VR.

Plugin Options

  • Tv
    The ProTV instance that the plugin should register to.

  • Skybox
    This is the material that the plugin should assign to the skybox material when the TV is considered ACTIVE. The required shader to use is the Video/Skybox shader that this plugin provides, as this plugin directly updates properties on the shader.

  • Fallback
    This is the material that the plugin should assign to the skybox material when the TV is considered INACTIVE. This can be left empty and the plugin will automatically use the world’s existing material as the fallback.

  • Brightness
    This is a UI Slider that is used to determine the gamma exposure of the rendered video in the shader. Helps reduce eye strain in certain situations.

  • The rest of the options are just for visual feedback of what state is active and do not have functional bearing on the plugin itself.


The plugin considers the TV to be ACTIVE for the following TV events: _TvPlay, _TvMediaStart
The plugin considers the TV to be INACTIVE for the following TV events: _TvMediaEnd, _TvStop, _TvVideoPlayerError

The following events handle updating the shader options for modifying the render output:

  • _Brightness
    Applies the value of the Brightness slider to the exposure value of the shader.

  • _Not3D
    Disables any 3D mode. Make the shader render the whole video frame to both eyes in VR.

  • _SideBySide
    Enables the SBS mode. This 3D layout mode is most commonly used for 180deg video.

  • _OverUnder
    Enables the OU mode. This 3D layout mode is most commonly used for 360deg video.

  • _Flip
    Makes the shader render the video inverted vertically. This setting generally shouldn’t be needed, but sometimes the video may need to be flipped on certain platforms or videos.

  • _SwapEyes
    Makes the shader render the video to the opposing eyes when in a 3D mode. When in desktop mode, this swaps the render between each eye, since desktop can only see one eye at a time.

  • _Deg180
    Sets the render area to 1/2 the skybox.

  • _Panoramic
    Sets the render area to the whole skybox.

  • _CubeMap
    Sets the render area to use the whole skybox as well as interpret the video as having a cubemap style layout instead of the equi-rectangular layout.

How to integrate AudioLink


Tips, Tricks and Best Practices

So you want to make your own Plugin?


How to use UdonGraph with ProTV

While ProTV generally uses U# for it’s setup, it is also provides and easy way to implement custom logic within UdonGraph as well.


Misc Notes

About YoutubeDL

In order for VRChat to determine what the direct URL is for a given video, it makes use of the tool called youtubeDL. This tool is what allows desktop to be able to “watch youtube videos” (or other hosting sites like vimeo/twitch/etc). Currently due to technical reasons, this tool is not available on the Quest version of VRChat, ergo why youtube doesn’t work on quest by default.

There are two ways around this limitation when dealing with quest.

  1. On desktop, manually resolve the url outside of vrchat. This involves downloading a copy of youtubeDL onto your local machine, opening CMD (or equivalent command line program), then running either of the following commands:

    • The simplest one-liner is youtubedl -g -f best
      This will simply get (the -g option) the url that is noted as the most compatible (the -f best option).
    • If you want to see what formats are available instead of just the “best” one, you can do youtubedl --list-formats
      This will list all options available, examine the media codec types and resolution in the output to find your desired option, then copy the format ID (for youtube it’s a plain number on the left of each option) and replace the “best” term with that ID.
      Like so: youtubedl -g -f 312
      (Also if you are using vimeo with this method, I highly recommend any of the formats that start with hls-akfire_interconnect_quic- as they are good quality and compatible with Quest as well)

    Once you’ve retrieved the long-form direct URL for the youtube video, copy it and paste it into the input field for the TV.

    And additional thing to note with this method is that for most major video hosting platforms (like youtube and twitch), the long-form url has an embedded expiration timestamp in the URL. This means that the URL will only work for up to that timestamp and then will start failing for people who either late join, or reload the video. For those who already connected and are playing the video, it’ll continue to play.

  2. If you have knowledge of self-hosting stuff and a bit of programming experience, you could easily make a resolver redirect that utilizes youtubeDL remotely to resolve the URL for whoever requests it.
    This requires a server to host it on (like a VPS, AWS, or whatever), a domain (optional) and a bit of knowledge with nginx and some server language.
    The result should be where the URL is of the server and within that URL (say for example, a query parameter) is the ACTUAL url you want resolved, the server target would resolve the actual url with youtubeDL and return a 301 redirect to the requesting client which would then play that new URL.
    This would work long-term as well because every time someone tries to (re)load the URL in the world, it would cause it to be resolved to the latest expiration timestamp, thus removing the issue mentioned in the first option.
    I have tested a very rudimentary version of this and it does work, with the exception of livestreams (like twitch) on Quest. For some reason that platform does not like being redirected to live media. I’m still looking into this particular issue.

Youtube caveats

  1. If you grabbed a bunch of youtube URLs to put into a ProTV playlist and some of them don’t seem to be working, be sure that you remove any &list=PLblahblahblahblahwhatever from the URL. If there is a youtube playlist ID as a query parameter on the url, youtubeDL will attempt to fetch ALL THE DATA from every single video within that playlist. If the youtube playlist is of excessive amount (like more than 10 items) it will cause excessive lag in loading the video, or may even just straight up timeout the request causing the video load to fail completely. TL;DR: ONLY HAVE YOUTUBE PLAYLISTS IN THE URLS IF YOU ABSOLUTELY NEED THEM TO BE.

Missing script references?

If you imported ProTV and it’s dependencies (latest SDK3 and UdonSharp 0.20.2 or later), try right clicking on the ArchiTechAnon folder and selecting “Reimport”. If that doesn’t work, try closing and reopening Unity, then doing a “Reimport” on the UdonSharp folder and on the ArchiTechAnon folder as well. This should correct any missing references that SHOULD be present. If it still isn’t working, contact me on discord to help chase down the actual cause.

Random tips to be aware of

  • When you call LoadURL/PlayURL on a video player instance, if the URL is invalid, the error callback will be called IMMEDIATELY before returning from the LoadURL/PlayURL method call.