Adding Shadertoy Embed with Invision Community

4 minute read

GameDev.net is built on top of the Invision Community software platform. The platform itself provides a decent PHP and Javascript framework and set of APIs that I’ve used to extend functionality for new features or modifications on GameDev.net, including:

  • GameDev.net Theme
  • New widget layouts
  • GameDev Projects
  • Job listings
  • and more…

Tonight we were discussing some site things in our moderator chat, and supporting Shadertoy embed popped up again in the conversation. I decided to do something about it and about 75 minutes later had Shadertoy embeds working on GameDev.net.

Shadertoy embed basically means when a user pastes a link to a Shadertoy project that the <iframe> for that project shows embedded in the post or content.

For example, I’ll embed this shadertoy here:

Embeds in Invision Community

Before this little project, I had never added new embeds to GameDev.net. Turns out it’s surprisingly easy once you understand how the Invision Community framework operates.

Step 1 - Understand Embed Parsing

The first thing to understand is that all text parsing ultimately goes through the \IPS\Text\Parser class, which is located in <root>/system/Text/Parser.php.

For embeds in particular, there are two possible routes:

  • oembed
  • custom

The oembed route is for any embed link using the oembed standard. Most of your popular sites use them, and in \IPS\Text\Parser the supported domains are defined in the oembedServices() static method.

The custom route is for everything else, which might include:

  • Internal links from your Invision Community site
  • Images
  • Videos
  • Anything else not using oembed

The custom route goes through the \IPS\Text\Parser::_customEmbed( \IPS\Http\Url $url, $iframe=false ) static method, which by default only looks at some Google links.

There is one other function worth noting: \IPS\Text\Parser::allowedIFrameBases(), which defines an array of valid embed links. These links can also be defined through the admin panel and stored in the database, but if we’re going to extend this functionality through code we might as well consider defining our URLs in code too.

Step 2 - Extend Parser with a Code Hook

So we know where to attack the problem. Now what?

Well like any good object-oriented language, we simply extend the methods above in the \IPS\Text\Parser class. In Invision Community you do that by creating a Code Hook in the Developer panel of an Application or Plugin. I’m going to focus on the embed development, so I recommend going to the Invision Community Developer Documentation for information on how to create an Application or Plugin and add a Code Hook.

In this particular case, Shadertoy does not support oembed, so we only need to be concerned with the _customEmbed() and allowedIFrameBases() methods, which we setup to override when creating our Code Hook.

Step 3 - Define Usage

This is pretty simple. We need to define how GameDev.net users are going to submit the Shadertoy embed links.

We want it to operate like all other embeds - meaning as simple as possible. Copy and paste the URL from your browser, and voila, embed. For example, if you want to embed a Youtube video you can copy the URL from the browser window you have open to the video, paste it into the GameDev.net editor, and after a second of ping/pong between the browser and server your Youtube video will show as embedded in the editor.

With Shadertoy the browser URL is different than the embed URL. This is pretty typical but worth noting as part of our TODO list.

Shadertoy URL’s in the browser look like this: https://www.shadertoy.com/view/MlfGR4.

But Shadertoy URL’s in an embed look like this: https://www.shadertoy.com/embed/MlfGR4.

Note the view vs. embed. Simple difference, but again worth noting.

Step 4 - Implementation

So we know how we want it to work, let’s implement the two methods in our Code Hook.

First, _customEmbed to return the embed <iframe> if a shadertoy.com URL is found.

static protected function _customEmbed( \IPS\Http\Url $url, $iframe=false )
{
    $result = parent::_customEmbed($url, $iframe);
    
    // verify this is a shadertoy url
    if (( $url->data['host'] === 'www.shadertoy.com') and 
        preg_match( '/^https:\/\/www\.shadertoy\.com\/view\//i', (string) $url, $matches ) )
    {
        // convert view to embed
        $embedUrl = str_replace('view','embed',(string)$url);

        // return our iframe, using shadertoy recommended settings
        return '<iframe width="640" height="360" data-embedContent data-controller="core.front.core.autosizeiframe" frameborder="0" src="'.(string)$embedUrl.'?gui=true&t=10&paused=true&muted=false" allowfullscreen></iframe>';
    }
    
    // only triggers if it's not a shadertoy embed
    return $result;
}

Next, allowedIFrameBases() to confirm that a Shadertoy link is a valid embed.

static protected function allowedIFrameBases()
{
    // grab full list
    $result = parent::allowedIFrameBases();
    
    // add shadertoy URL to allowed array
    $return = array_merge( $result, array(
        'www.shadertoy.com/view/',
    ));
    
    return $return;
}

Step 5 - Go Live and Announce

And that’s it. After testing a few scenarios locally to make sure I didn’t forget anything, I built the plugin in my development environment, downloaded it, and uploaded it to the live GameDev.net server. Once it was live and confirmed, I made a post in the Graphics forum to let everyone know it existed.

Hopefully some members find this small feature useful!

Categories:

Updated: