Skip to content

Instantly share code, notes, and snippets.

@gre
Forked from gre/article.md
Last active August 29, 2015 14:02
Show Gist options
  • Save gre/4c57de495ca405bffd5a to your computer and use it in GitHub Desktop.
Save gre/4c57de495ca405bffd5a to your computer and use it in GitHub Desktop.

The GLSL.io initiative aims to build an Open Collection of GLSL Transitions. It aims to be highly community-driven and free-software.

<iframe width="853" height="480" src="//www.youtube.com/embed/BYdCNTJaGss" frameborder="0" allowfullscreen="allowfullscreen"></iframe>

No data are kept on our servers, data is all stored in Github Gists. The project is yours, up to you to deploy your own platform instance if you like to. The official platform will be hosted on glsl.io. Source code is available on GitHub, contribution is welcomed.

What is a "GLSL Transition"?

A GLSL Transition is a Transition performed using OpenGL Shading Language (GLSL). A Transition is an Animation Effect moving from one image to another.

Why GLSL?

GLSL is efficient because it compiles to the graphic card and is very powerful, not limited to simple transformations, but with unlimited possible draw (in a functional way). Also, GLSL is available not only for the Web in recent browsers but also in native environnement via OpenGL.

For which use case?

You may need GLSL Transitions for making web image animations (e.g. a slideshow) or for transitions in a Video Editing Software.

We wish to interoperate with existing Video Editing Software soon. If you want to contribute or if you have another use-case idea, feel free to contact us.

How to use a GLSL Transition?

  • For the web, you can use the JavaScript GLSL Transition Library available on GitHub and on NPM.

Example:

var createTransition = GlslTransition(canvas);
var glslCode = "#ifdef GL_ES\nprecision highp float;\n#endif\n \n// General parameters\nuniform sampler2D from;\nuniform sampler2D to;\nuniform float progress;\nuniform vec2 resolution;\n \nuniform float smoothness;\nuniform bool opening;\n \nconst vec2 center = vec2(0.5, 0.5);\nconst float SQRT_2 = 1.414213562373;\n \nvoid main() {\n  vec2 p = gl_FragCoord.xy / resolution.xy;\n  float x = opening ? progress : 1.-progress;\n  float m = smoothstep(-smoothness, 0.0, SQRT_2*distance(center, p) - x*(1.+smoothness));\n  gl_FragColor = mix(texture2D(from, p), texture2D(to, p), opening ? 1.-m : m);\n}";
var transition = createTransition(glslCode);
function animate() {
  var uniforms = { smoothness: 0.3, opening: true };
  var easing = function (x) { return x; } // linear
  var duration = 1000; // 1 second
  return transition(uniforms, duration, easing);
}
animate()
  .then(...) // it's a promise :-)

Slideshow example in glsl-transition example.

What technically is a GLSL Transition?

A GLSL Transition is a GLSL Fragment Shader that must have these uniforms:

  • two sampler2D, from and to which are the 2 images (or videos) that the transition has to move from one to the other.
  • a vec2 resolution which is the resolution of the canvas.
  • a float progress which moves from 0.0 to 1.0 during the transition.

A GLSL Transition can also have custom user parameters by defining extra uniforms (which can be float, int, bool, vectors, matrix). It allows to make a transition much more customisable and to generate an infinite number of variants of a same transition.

How to create Transitions?

The editor is a playground for you to experiment with GLSL shaders. You can also study existing transitions from the gallery.

For creating content, you must login with a Github account. When you Create a new GLSL Transition from the editor, it creates a new Github Gist with your account. You can Save your Transition as much as you like, it will patch the original gist (so you have a trace of the history). When you are done, you can Publish your Transition to be visible in the gallery.

NB. A GLSL Transition entry is technically created by forking a "root" Gist. We can easily list all GLSL Transitions by listing forks of this root Gist.

GLSL Transition Terms

Here are the main rules of a GLSL Transition:

  1. It MUST be a transition: the "from" image is displayed when progress=0.0, the animation should smoothly move to the final state which is the "to" image, displayed when progress=1.0.
  2. It MUST remain free software (MIT License attached with the Gist).
  3. It SHOULD be original and unique. Identical or very similar should not already exists.
  4. It SHOULD be yours. Fork or code inspiration are however allowed if it doesn't invalidate rule 3. and if the changes as sufficiently important.

Please make your transitions comply with these rules to stay in the Open Collection.

What is the future for GLSL.io?

GLSL.io will stay free and open-source as all the GLSL Transitions available on GLSL.io.

There is tons of ideas and features that we want to add to this initial version. We are focusing on stabilisation but we are going to work by sprint to provide frequent new releases. We are going to clarify the roadmap and features in the next days. For more information, refer to the Github project issues and feel free to contribute to the project.

We are eager to work with existing Video Editor Software to make them compatible with these GLSL Transitions (e.g. via plugins). For instance, having a ffmpeg filter would be awesome.

Contact us

As you may have noticed GLSL.io is entirely based on Gists... Even the blog of GLSL.io! Let's see how.

Goal

I wanted for GLSL.io a dead simple and easy to maintain blog engine. I'm a huge fan of Jekyll, but it wouldn't apply here: I want to write articles without redeploying the whole application.

GLSL.io is heavily based on Gists, so why not using one gist for storing articles?

So I have this gist: https://gist.github.com/gre/4c57de495ca405bffd5a. And in 2 hours I was able to implement a blog engine with articles – each article have a date, a title and rich and semantic content.

Each file is an article. The filename take the Jekyll convention (e.g. 2014-05-19-What_is_GLSL.io.md) which is used to store the date and the article title. The content of the file is just the article using Markdown syntax.

Now, I can maintain my articles using Atom and live Markdown viewer, save the file and push to the Gist. (yes, a Gist is a git repository):

Server-side: Retrieving, transforming and caching the Gist

The server of GLSL.io is made using Playframework and using the Scala language.

Here is the code which just handle the Gist JSON and Markdown transformation and caching:

val ArticleTitle = """(\d{4})-(\d{2})-(\d{2})-(.*)[.]md""".r

val articlesGistReader = Reads { json: JsValue =>
  val transformer = new ActuariusTransformer()
  (json \ "files").asOpt[JsObject].map(files => JsSuccess(JsArray(files.fields.sortBy(_._1).reverse.flatMap { case (filename, obj) =>
    (filename, obj \ "content") match {
      case (ArticleTitle(year, month, day, title), JsString(content)) =>
        Some(Json.obj(
          "year" -> year.toInt,
          "month" -> month.toInt,
          "day" -> day.toInt,
          "title" -> title.replaceAll("_", " "),
          "content" -> transformer.apply(content)
        ))
      case _ =>
        None
    }
  }))).getOrElse(JsError("Invalid Gist JSON"))
}

def articles = Cached((_:RequestHeader) => "articles", 120) {
  Action.async {
      GistWS.get("4c57de495ca405bffd5a") // trivial WS code behind
      .map(_.transform(articlesGistReader))
      .map(_.fold(
        err => InternalServerError,
        json => Ok(json)
      ))
  }
}

Client-side: displaying the Articles with syntax color

GLSL.io recently moved to React on the client-side. This is a very powerful framework that I recommend everyone to give a try. It is also using Browserify (CommonJS style for the browser) which is awesome used with React.

I have 2 components: Article and Articles. And a blog screen entry point (in order to hook in the GLSL.io application). There is also a bunch of CSS that I won't detail here.

JavaScript Entry point:

var Qajax = require("qajax");
var Articles = require("./articles");
function show () {
  return Qajax("/api/blog/articles")
    .then(Qajax.filterSuccess)
    .then(Qajax.toJSON)
    .then(function (articles) {
      return Articles({ articles: articles });
    });
}
function init () {
  return {
    show: show
  };
}
module.exports = init;

You can notice the use of Promise which leads to a very straightforward and powerful code.

Articles:

/** @jsx React.DOM */
var React = require("react");
var Article = require("./article");

var Articles = React.createClass({
  propTypes: {
    articles: React.PropTypes.array.isRequired
  },
  render: function () {
    var articles = this.props.articles.map(function (article) {
      return Article(article);
    });
    return <div className="articles">{articles}</div>;
  }
});

module.exports = Articles;

and finally,

Article:

/** @jsx React.DOM */
var React = require("react");
var hljs = require('highlight.js');

var MONTHS = {
  1: "January",
  2: "February",
  3: "March",
  4: "May",
  5: "April",
  6: "June",
  7: "July",
  8: "August",
  9: "September",
  10: "October",
  11: "November",
  12: "December"
};

var Article = React.createClass({
  propTypes: {
    year: React.PropTypes.number.isRequired,
    month: React.PropTypes.number.isRequired,
    day: React.PropTypes.number.isRequired,
    title: React.PropTypes.string.isRequired,
    content: React.PropTypes.string.isRequired
  },
  render: function () {
    var year = this.props.year;
    var month = this.props.month;
    var day = this.props.day;
    return <article>
      <header>
        <h1 className="title">{this.props.title}</h1>
        <time datetime={year+"-"+month+"-"+day}>{MONTHS[month]} {day}, {year}</time>
      </header>
      <section ref="content" className="content" dangerouslySetInnerHTML={{__html: this.props.content}} />
    </article>;
  },

  componentDidMount: function () {
    var node = this.refs.content.getDOMNode();
    // Highlight codes of the Article
    node.querySelectorAll("pre code").forEach(function (code) {
      code.innerHTML = hljs.highlight("javascript", code.innerText).value;
      code.className += " hljs";
    });
  }
});

module.exports = Article;

Hack your solution

Using Gist as a blog content repository is a simple solution which works perfectly for my use case.

Find the solution which works the best for you, and enjoy!

Going farther

I haven't implemented so much features yet, and this is fine for now, but I had some idea in mind that this architecture could easily allow:

  • Versionning : We could easily handle new article releases by just switching the Gist commit hash (instead of taking HEAD). See how we can easily browse different revisions: https://gist.github.com/gre/600d8a793ca7901a25f2/revisions.
  • Cachable forever : the previous point allows to make the articles cached forever. I'm talking both about the server side (a request to Gist done once per version, no recurring check). Same for the client-side, e.g. we could cache the /api/blog/articles in localStorage until the revision version changes.
  • Multi-users : Supporting multiple gists (by just merging them) could make a community blogging system, especially if using the Gist Fork system.

And some features:

  • Pagination : Of course we will also need pagination. I'm not doing it yet because I don't have 20 articles yet.
  • Article page : A simple feature could be to add one dedicated page for an article. It is then eaisly bookmarkable.
  • Cache each article independantly to polish performance of Pagination and Article page (in term of brandwidth mainly).
  • Attach metadata to an article could be done with an extra JSON with the same filename.json. Alternatively, we could support a Jekyll like header syntax in the markdown (Bonus is Github renderer should display it properly).

This video tutorial is a quick look into GLSL Transition basics and fragment shader concepts. It also implements a simple GLSL Transition using luma texture.

<iframe width="853" height="480" src="//www.youtube.com/embed/3mAsROQCYU8" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment