Embedding shadertoys in website

NB: In code snippets below, replace { by < . WordPress is unable to display code. ūüė¶

Just as clickable image:

Copy-paste the shader URL, and build the one for the corresponding icon:

{ a href="https://www.shadertoy.com/view/SHADER_ID" >{ img src="https://www.shadertoy.com/media/shaders/SHADER_ID.jpg" /> My shader { /a >

My shader

Functional shader:

If you click the “share” button below a shader, you get the piece of code to copy-paste:

{ iframe src="https://www.shadertoy.com/embed/SHADER_ID?gui=true&t=10&paused=true&muted=false" width="640" height="360" frameborder="0" allowfullscreen="allowfullscreen" >{ /iframe >

( no example, for WordPress doesn’t accept iframes ).

… As webpage background:

  • To the code above in your html, just add a css file or entry telling to map the iframe as full-window background. See the 3 tabs html, css and result in¬†¬†example.

    Minimal version (would be better to specify a class or id name):

    iframe {
    position: fixed; width: 100%; height: 100%; top: 0; right: 0; bottom: 0; left: 0; z-index; -1; pointer-events: none; }

  • Or : Shader Web Background

Fetching Shadertoy database via javascript:

See the API manual here.
You first need to register online your USER_KEY to be mentioned in your scripts. Then you can fetch queries to the Shadertoy data base to recover as JSON files lists of shader ids via search criterion, or shaders description and contents, to be used in you javascript. For instance, this is how I did my own shaders browser.

Note that only shaders saved as “public+API” can be managed. In particular, you can’t access unlisted private shaders.


Avoiding compiler crash (or endless compilation)

Sometime, shader compilation is long. Or ultra-long. Or freezing the browser. Or even crashing it after a timeout. Worse: this can happen for other peoples (often under another OS) on your shaders while it was okay for you, then your shader can be unlisted because of something you don’t experience (very frustrating).

Before suggesting solutions and what to care about, it’s important to understand…

What happens at early compilation

  • Functions do not really exist on GPU, because there is no stack to jump out then go back (that’s why recursivity is not allowed). This is just a writing aid, like macros. So all functions are first inlined.
  • Loops used to be fake as well. But even now that dynamic loops do exist, optimizers strongly prefer to keep unrolling them for performances: loop content is duplicated as many times as loop steps, with loop variable replaced by its successive const values. One problem is that optimizers don’t foresee that it might overwhelm resources (starting with final code length).
  • Branching vs divergence: when in a same warp (i.e. 32 pixels neighborhood) different conditional (“if“) branches are followed, SIMD parallelism force each thread to run them all (masking the result when not the right branch for a given thread), as shown in these¬†demos.¬† For variable length loops (for, while) or early exit (conditional break in a loop) this can be even more involved.
    This firstly impact runtime performances, but branches obviously also lengthen the inlined code length (e.g. if big functions are called in branches).
    Also,¬† while dFdx, dFdy, fwidth might just give silly values or get unset/reset across diverging pixels, on some systems the function texture() try to do better to find the MIPmap LOD to use,¬† which may consist in evaluating the whole code 4 time to recover a 2×2 neighborhood on which evaluating the derivatives of texture coordinates.
  • The resulting functionless and (almost) loopless simplified but very longer GLSL code is then really compiled and optimized. But the code length and compile duration might overwhelm resources and fail, causing a crash.
  • Note that before compilation, Angle applies various code modifications to turn around some bugs occurring on some drivers/boards, then on Windows it transpiles GLSL to HLSL. And after, both shading languages are compiled into intermediate assembly language ARB, to be compiled and optimized again to get a true GPU executable. So in total there is a full stack of code rewrite and optimization.

Now, just consider the big figure: e.g. for a ray-marching code with a long stepping loop, containing branches (e.g. “if hit”) calling functions (to get the normal, the textures values, etc), that might themselves contain loops on function (e.g. for procedural texturing). Worse: the shading part launching shadow rays (or reflected/refracted rays) with a brand new marching loop (yes, it would be duplicated for each step of the main loop). In addition to the “map” function testing the whole scene for ray-intersection at every step, and this one is likely to also contain loops and further functions call.
The true code length before the true compilation is the huge combinatory of all this. You have no idea how long it could be. Well, indeed you have to.

What can we do ?


  • Do you really need 1000000 steps ? sure ?
  • Do you really need to detail procedural texture (or shape) down to nanometer ? (think about where falls the pixel size limit).
  • Do you really need to compute the texture also for shadow evaluation ?
  • Can’t you first test a raw hit, then inspect the details once this step is reached ?
  • Can’t some part be done in as separate buffer (i.e., stored for the whole frame rather than evaluated for each pixel) ? BTW, does it really need to be re-evaluated at each time step ?
  • Can’t a repeated pattern be done implicitly with a simple mod/fract rather than with an explicit loop ?
  • Or can’t you find the only one (or few) items that can really meet the pixel ?

Within the unroll & inline logic

  • Deferred heavy¬† processing out of loops:
        if (end_condition) { process; break; }
        if (end_condition) { set_parameters; break; }
    then process the parameters after the loop.
    Typically: shading evaluation, shadows, reflected rays…
  • Deferred heavy¬† processing out of branches:
        ...else if ( cond_N ) do_action(params);
        ...else if ( cond_N ) set_parameters;
    then process the parameters after the loop.
  • Specialize functions, or use branches inside only if triggered by const params.
    Worst case would be an shape(P, kind, params)  implementing a whole bank of possible shapes called into a ray-marching loop: if kind is not const, the whole shape() source will be multi-duplicated.
  • Don’t call¬†texture() in any divergence-prone area (“if” branch, variable-length or early breakable loop), at least if MIPmap is activated. Or use explicit LOD via¬†textureLod() or¬†textureGrad() .

Keep your critical judgement: the above advices are not always possible, and not always useful. Small loops, small processes don’t deserve special action, plus the GPU *is* powerful enough to deliver good performances on complicated code. Just, learn to recognize the coding patterns that make two “similarly complicated” shaders (by the number of lines or functions)¬† having totally different fate by how the compiler react. And avoid blindly following the dark path, the one nastily looking “as you would have done on CPU”.

Fighting the unroll & inline logic

You can also fight loop unrolling by making the compiler unable to know the length. E.g.:
    for (int i=0; i<N+min(0,iFrame); i++)

You can forbid optimizations [ which, exactly ? and is it really working ? ] by adding at the top of each code, or later but still outside functions definition :
    #pragma optimize(on)
    #pragma optimize(off)

Compilation can be a lot faster, but of course runtime perfs will be impacted.


See also:

Playable games in Shadertoy !

Yes, even if we are stuck in pixel shader and have no access to input data, many people managed to program games in Shadertoys ! From basic to huge (might not always compile on your machine ūüôā ), from pro to amateur, 164 are there : (including a few not-playable demos)
( recommanded: HoverZoom plugin over previews )

Action games (platform, shooter, exploration)

Adventure & strategy games

Simulation, driving, sport games

Arcade & casual games

Puzzle games

Board, cards & paper games

But so many classical games are still missing, comprising simple ones : will you dare to program one ?¬† ūüôā ( remind to add the tag “game” ).


Key shortcuts

Did you know that ShaderToy GUI as well as it’s CodeMirror shader editor had many key-shortcuts ? Seems that most people ignore it (despite GUI ones pop-up as tool-tips… if you try).


  • Cmd = “microsoft” or “apple” key.
  • Ctrl¬† might be overridden by your browser.
  • Mac: replace Ctrl by Cmd, or sometime by Alt or Ctrl-Alt.

GUI key shortcuts

  • “Down”, “Cmd Down”:¬† resetTime
  • “Alt Up”, “Cmd Up”:¬† ¬† ¬† ¬†pauseTime

  • right menu:¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†save or copy image
  • “Alt+r” :¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† video record

  • “Ctrl S”, “Cmd S”:¬† ¬† ¬† ¬† ¬†Save shader
  • “Alt Enter”, “Cmd-Enter”: Compile shader
  • “F5” :¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† refresh page ( indeed it’s a browser shortcut )

Editor key shortcuts

Editor GUI

“Alt F”:¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†Editor in FullScreen
“Alt Right”, “Cmd Right”:¬† ¬† ¬†Go to Right¬†Tab
“Alt Left”, “Cmd Left”:¬† ¬† ¬† ¬† ¬† Go to Left¬†Tab
“Alt -“, “Cmd -“:¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†decrease FontSize
“Alt =”,” Cmd =”:¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†increase FontSize

Basic obvious keys

Left, Right, Up, Down,      Home (of line), End (of line),      PageUp, PageDown
Delete, Backspace, Shift-Backspace,       Tab,       Enter,       Insert

extra key shortcuts

  • basic emacs shortcut

  • “Ctrl-S”: “save”,

  • “Ctrl-F”: find,¬† ¬† “Ctrl-G”: find Next,¬† ¬† “Shift-Ctrl-G”: find Prev,
    “Shift-Ctrl-F”:¬† replace,¬† ¬† “Shift-Ctrl-R”: replace All,

  • “Ctrl-Z”: undo,¬† ¬† “Shift-Ctrl-Z”,”Ctrl-Y”: redo,

  • “Ctrl-A”: select All,¬† ¬† “Ctrl-U”: undo Selection,¬† ¬† “Shift-Ctrl-U”,”Alt-U”: redo Selection, “Esc”: single Selection ( ? )

  • “Ctrl-Home”,”Ctrl-Up”: Doc Start, ”¬† ¬†Ctrl-End”,”Ctrl-Down”: Doc End,
    “Ctrl-Left”: Word Left,¬† ¬† “Ctrl-Right”: Word Right,
    “Alt-Left”: Line Start,¬† ¬† ¬† “Alt-Right”: LineEnd,

  • “Ctrl-D”: delete Line,
    “Ctrl-Backspace”: del Word Before,¬† ¬† “Ctrl-Delete”: del Word After,

  • “Ctrl-[“: indent Less,¬† ¬† “Ctrl-]”: indent More,¬† ( NB: not working for me )
    “Shift-Tab”: indent Auto (and replace tabs by spaces)


2 more for mac only:

  • “Cmd-Backspace”: del Wrapped Line Left (?), “Cmd-Delete”: del Wrapped Line Right (?)

Extending or replacing Shadertoy

Extending: Plugins and scripts

Plugin “Shadertoy unofficial” (unrelated to this site ūüôā )

Features:¬† ¬†extend and fix what you dreamed of ūüôā

  • Fork any shader.
  • Backup all your shaders, import or export shaders.
  • Adjustable slider for full control of ‘iTime’ uniform and audio/video inputs + loops.
    4 sliders simulating mouse position.
  • Make link clickable. Make key shortcut work even in fullscreen.
  • Speed-up button for simulation-based shaders.
  • Image preview (to compare what you see with what the coder saw).
  • List of your own recent visited shaders (life-changer for finding again private and unlisted shaders ūüôā ).
  • Change resolution in windowed and fullscreen mode by pressing keys 1…9.
  • Take screenshot width doubled resolution.
  • Pause/Restart in fullscreen mode.
  • Fullscreen edit mode.
  • and more

Scripts for TamperMonkey plugin (all browsers)

  • Andrei Drexler proposes several goodies (some have then be included in Shadertoy):
    Comments preview, browse errors, first-person mouse, enhance video recording, display perfs, limit framerate, force webGL1,…

Using custom textures (only on your local machine)

Exporting images and video

Shadertoy now natively allows to save images and videos of the result, and exr images of the buffers (including CubeMapA).
In addition:

  • The Shadertoy unofficial plugin add some goodies ( sur-resolution, etc).
  • The Shadertoy frame exporter allow to choose format, fps, duration, and save the video as files.
  • The Koltes Digital frame exporter [ obsolete ? ]
  • As an alternate, one can convert a saved webm video from shell using
    ffmpeg -i capture.webm -vf fps=30 -filter:v "scale=910:512, crop=512:512" frame%3d.png

360 images and images with depth (e.g. for Facebook)

Many mobile phones can now provide cylindrical or spherical panorama is “360¬į” format, as well as images with depth. Facebook, for instance, can display them natively ( only as image in a front post for now, not in a reply ).
How to produce them with Shadertoy ?

“360” images:

  • from CubeMapA:
    • click the “save image” button of CubeMapA. Note that the format is exr.
    • convert the .exr to .jpg with your favorite tool ( ImageMagick convert, Gimp, etc ).
    • add the “360” metadata tag to the image:
      exiftool -ProjectionType="equirectangular" <image>.jpg
      The tool can be found here: https://exiftool.org/  or on linux, just search in packages list.
  • from the Image tab:
    Compute the projection as in this shader,  but commenting out the #define STEREO line 156

Images with depth:

  • Making a native file from RGB + depth in A, or depth as a 2nd image: I don’t know.
  • In Facebook: just download 2 images:¬† ¬†<name>.jpg and <name>_depth.png

Pseudo-scripts (via URL, using bookmarks)
[ no longer allowed ]

(… but you can still copy-paste them in the JS console via F12 . )

Theron Tarigo (aka TTG ) propose several goodies here ( + corresponding shadertoy ) :

  • Create a shader (+content) with a simple bookmarkable link ( e.g. your custom default shader )
  • Load them (or first, export) from a bookmarkable JS ( if enabled on your browser ).
  • Overwrite an existing shader.

Replacing: Websites and Desktop apps

Shadertoy cousins

Fragment shader: (like shadertoy)

  • Shaderoo¬† ¬† ¬† ¬† ¬†quasi-clone. Goodies:¬† geometry shader, infinite amount of buffers, external textures, includes, save geometry, comments in latex or images.
  • shaderboy ¬†¬†¬†¬†¬†¬† quasi-clone (but without textures and cubemap yet), especially convenient on smarthphone, can be installed standalone, shaders stored on GoogleDrive.¬† Goodies: GUI for uniforms (+ 24 extra), export video and sounds. Keymap, features and compatibility.
  • ISF¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†Goodies: vertex shaders, sliders, modules, textures, integrable

no buffers, no textures:

  • glslsandbox¬† ¬† ¬†Goodies: clone, camera
  • GLSLbin¬† ¬† ¬† ¬† ¬† ¬†Goodies: includes ( from many base stack.gl¬†shaders )
  • OneShader¬† ¬† ¬† ¬†Goodies: extra sliders for uniforms with default value
  • ShaderGif ¬†¬†¬†¬†¬†¬†¬† Goddies:

Applied on a mesh: ( + vertex shader, possibly )

  • shdr@bkcore¬† Goodies: vertex shaders, snippets, camera, 3D models, custom models
  • shaderfrog¬† ¬† ¬† Goodies: shaders + composer, vertex shaders, clone, uniforms,¬†camera, 3D models, custom models
  • shader_editor@kickjs¬†Goodies :¬†vertex shaders, load texture, uniforms, 3D models
  • vertexShaderArt¬† ¬† Goodies: tune vertex position and color at the same time.

On desktop (quick shader prototypers):

  • KodeLife¬† ¬† ¬† ¬† ¬† Goodies: vertex, tesselators, geometry, fragments, custom model, Shadertoy compatibility mode
  • glslEditor
  • shaderEd¬† ¬† ¬† ¬† ( also have an online lite version )
  • shaderboi ¬†¬†¬†¬†¬† Run shadertoys offline, + output goodies ( used on the demoscene ).

Applications compatible with Shadertoy

  • Shadertoy iOS app.
  • libShadertoy¬†(Ubuntu¬†and Debian) : run shadertoys from your desktop app.
    Also a simple way to play with fragments shaders on desktop without all the burden (bindings…), with the Shadertoy convention for extra uniforms (mouse, time…).
    Can mix GLSL, CUDA, C++ passes.
  • Vulkan shadertoy launcher (Windows & Linux): run shadertoys from your desktop app using vulkan.
    Note that this allows to replace floats with doubles (and vec2 with dvec2 , etc) to improve precision.
  • ShadertoyConnector¬† (Ubuntu¬†and Debian) : interface with Mathematica and Octave
    ( send parameters and arrays to your shader, get the resulting image as array).
    Also a simple way to interface GLSL shaders with Mathematica and Octave.
  • Accessing shadertoy database from your website or appli: web API.
  • Note that some programs are more or less shadertoy-compatible:
    Gratin, Natron, KodeLife .
  • Grimoire¬†(Linux, MaxOS, Windows): command-line tool for creating shader demos
  • Shader Web Background : get a shadertoy on webpage background.
  • TurboPlay : multimedia editor.
  • shadertoy plugin for VisualStudioCode


Note that Firefox and Chrome now allow direct editing (and more) of any online shader:

  • Activating the shader developer tool on¬†Firefox
  • Equivalent plugin on¬†Chrome

WebGL 2.0 vs WebGL 1.0

on February,15 2017, Shadertoy moved to WebGL 2.0. What does it change ?

NB: WebGL 2.0 corresponds to OpenGL ES 3.0 , which is derived from OpenGL 3.3 and 4.2 , minus some features.  Cf specs or quick card, or cheesy slides.  Test availability on your browser, and among users.

What new does it bring to Shadertoy ?

New features, … and new constraints.
And new compatibility issues: see last section there.

New features:

  • Arrays: ¬†(still 1D only ) ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†example
    • dynamic indexing ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†A[k]
    • initialization ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†float[] A = float[] ( 17., 23.4, 56.3, 0., 7. );
    • implicit sizing ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†size = A.length(); ¬† ¬† ¬† ¬† ¬† ¬† ¬†\ ¬† ¬† ¬† or float A[]
    • a function can return an array. ¬† ¬† ¬†float[5] foo() { }
      A reminder that there is very little memory per thread: don’t abuse of¬†arrays !
  • All int operations: ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†examples ¬†1 , 2¬†
    • bits and logic: & , | , ^ , >> , << , ¬†&= , |= , ^= , >>= , <<=
    • unsigneds: ¬† uint, uvec, 1234U,¬†0x3f800000U ¬†( but 0b010101 is still missing )
    • % (i.e., mod) , %= , abs()
  • Flow control:
    • while(){}, ¬†do{}while(),
      switch(int){case:default:} ¬† ¬† Some bugs on Windows. ūüė¶ Avoid return¬†inside.
    • Loops bounds no longer need to be constant.
      (attention: const loops might still be inlined if the compiler thinks it optimizes… even if this cause compiling timeout or too long shader.
      Hack bound if you want to forbid inlining: 100+1e-30*iMouse.x ).
    • Functions can still not be recursive (they are still inlined, since there is no stack on GPU. Or manage one yourself with arrays if you really need. example ).
  • Formatting:
    • defines can be multiline ! ¬† ¬† ¬† continuation to next line mark: ¬† \
      Not compiling on firefox for now ūüė¶¬† [apparently now fixed]
    • UTF8 allowed (really everywhere ? possible issues on the right of #define )
    • more float check at compilation time. ¬† ¬† ¬† ¬†example
  • New matrix and vector operations and types:
    • mat inverse(), determinant(),¬† transpose()
    • mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
    • outerProduct()
    • cross(vec2) is still missing. Worse: you can no longer overload it.
  • New math operators:
    • hyperbolic funcs: sinh, cosh, tanh, asinh, acosh, atanh
    • trunc(), round(), roundEven(), ¬†f=modf(v,i), isnan(), isinf()
    • [un]pack[S|U]norm2x16() ,¬†[un]packHalf2x16() : ¬†pack vec2 in uint and back
      [U]intBitsToFloat and back:¬†convert to int comprising special floats¬†(NaN, Inf…)
    • be careful: you can no longer overload (e.g. defining min(struct,struct) ).
  • New textures operations:
    • sampler: 2D or 3D
    • textureLod()¬†( previously an EXT ) ¬† the one to use to control MIPmap level¬†
    • textureGrad() ¬†(previously an EXT) ¬†the one to use if you have dFdX,dFdY
    • texture*Offset()
    • texelFetch() ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† avoid any interpolation, e.g., to use a texture as an array
    • textureSize() ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† useless since more chars than iChannelResolution[n] :-p

New constraints:     Verify your previews shaders, they might be broken !

  • Globals initialization must really be constant. Uniforms (like iGlobalTime , iResolution, iMouse), and even sqrt(3.) or float a=1.,b=a; ¬†are no longer allowed… but for const variables.
  • It’s now totally forbidden to reuse a reserved keyword (built-in funcs, etc) as variable or function name. You cannot even overload functions. E.g.,¬†
    • min(struct1, struct2) or cross(vec2,vec2) are no longer licit
    • you didn’t know new keyword will exist !
      frequent conflict: sample, smooth, round, inverse …
  • “precision” is no longer allowed… even in comments !
  • texture2D*() is now texture*() ¬† (the team has already patched your shaders for this).
    But guessing the implicit LOD is now an error on windows in non-unrollable loops, and generates a lot of extra code anyway (+ possible compiler freeze). 
    -> now prefer texelFetch or textureLOD (at the price of WebGl1 compatibility).
More GLSL ES 3 reserved keywords:

Possibly/soon available to ShaderToy ?
invariant layout centroid flat smooth
lowp mediump highp precision
sampler2D sampler3D samplerCube
sampler2DShadow samplerCubeShadow
sampler2DArray sampler2DArrayShadow
isampler2D isampler3D isamplerCube isampler2DArray
usampler2D usampler3D usamplerCube usampler2DArray

Keywords reserved for future use:
attribute varying coherent volatile restrict readonly writeonly resource atomic_uint
noperspective patch sample subroutine common partition active
asm class union enum typedef template this goto
inline noinline volatile public static extern external interface
long short double half fixed unsigned superp
input output sizeof cast namespace using
hvec* sampler3DRect  filter

Compatibility issues in Shadertoy / webGLSL

[ New 28/02/2017 :  WebGL2.0 compatibility issues. See last section. ]

Sometimes, somebody else shader looks strange, or blank, or broken, on your machine.
Conversely, if your shader works on your machine, it’s not a proof that it works elsewhere.

Usual suspects:

  • Noisy image:¬† –¬†Variable not initialized.
  • Blank image:¬† –¬†Negative parameter for log, sqrt, pow.
                               Рclamp(min,max,v) instead of clamp(v,min,max).
                               Рsmoothstep(v, v0,v1) instead of smoothstep(v0,v1, v)
                               Рout parameter used instead of inout.
                               Рmod(x,0.) or atan(0.,0.)
    ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†–¬†MIPmap on grey image on firefox.
  • Compilation error (assuming it was ok on author’s system):
    ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†–¬†Bug in your compiler (e.g. const struct)
    ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†–¬†too permissive author’s compiler (global initialized with not const expression)
    ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†–¬†Your system has less than 32bits and a const value is more.
                               РShader too costly to compile on your system.
  • Browser or system freeze (or crash):
    ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†–¬†Shader way too costly for¬†your system.

More details are given below.

Fast medic-test: include this Common tab . If it does fix the problem,  progressively remove parts to find the guilty command.

Classical Reasons:

There is a full stack of subsystems digesting your shadertoy GLSL¬†ES source before it reaches the GPU: the shadertoy API, the web browser, the OS, the OpenGL handler, the 3D driver, the GPU driver, its GLSL/HLSL compiler, the GPU. ¬†Some driver embedded compilers have different behaviors and different bugs, but it’s even worse. E.g., Windows use either nativeOpenGL or Angle which¬†translate your GLSL source to HLSL (!) ¬†(to switch to¬†OpenGL:¬†Firefox ‚Üí about:config ‚Üí¬†webgl.disable-angle = true, webgl.force-enabled = true)¬†while linux and macOS use (better) native OpenGL. On Windows, Chrome and firefox don’t even rely on the same version of DirectX3D. Some high end tablets like the Ipad use crude GLSL implementation. Browsers override some format flags for textures and buffers. etc,etc,etc.
Any of these can cause issues, so from here we will call this stack “your system”.

  • Some systems¬†implicitly initialize variables and some others not (in the spirit of the spec).
    => Always initialize variables. Comprising “out” parameters.

    • Note that on iOS and old Mac operations like v -= v¬† won’t be sufficient as initialisation to 0 since sometime the undetermined initial value is not even a valid number ( and NaN – NaN is not zero ).
  • Some systems implicitly extend the validity of invalid operations like log, sqrt or pow of negative, mod(x,0.), atan(0.,0.), and not the others (following the specs).
    => Never use negatives for log, sqrt, pow. (prefer x*x if you want to square).
    ¬† ¬† ¬†¬† Don’t mod on 0.
    ¬† ¬† ¬†¬† Don’t ask for the angle of a null vector. => atan(y,x+1e-15)¬†
           clamp API is (v,min,max).
    smoothstep API is (v0,v1, v).
  • Some systems loosely implement the spec, making inout for out.
    => Be strict on code requirement; do initialize out parameters, do use inout if incoming value is expected.
  • Some system are more picky than others. For instance grey-level textures implemented as “luminance” are not “renderable” according to the spec, thus not compatible with some operations like MIPmap. Apparently only Firefox is so picky, resulting in blank values.
    => Switch to a colored texture or replace MIPmap flag by Linear.
  • WebGL2 is a lot more picky. See¬†WebGL 2.0 vs WebGL 1.0

There are genuine bugs and hard limitations of some systems:

  • Shadertoy buffers are supposed to be 16bits floats, but some browsers (e.g. chrome) override this for 32bits floats. A shader writer on chrome will not see overflows.
  • Complex expressions including redefinition of variables ( e.g., x = (x=y)*x ) may not be evaluate in the same order on some systems (typically, after translation to HLSL by Windows Angle; API drivers can also have problems), or can even be bugged (O += x -O on some old systems). ¬†(OpenGL is wrongly evaluating first all the subdefinitions while here Angle is right.)
    => for wide compatibility, avoid reusing the same variable redefinition within a single expression Рwhich span includes comma-separated expressions.
  • cond ? op1, op2 : op3, op4¬† is not always compiled the same: op1,op2 is ok, but add parenthesis to (op3, op4) or it may be compiled as ( cond ? op1, op2 : op3 ), op4¬† on some systems. It might even depends on further context.
  • Low-end devices or old systems sometimes not implement the full IEEE math such as NaN (indeed, no system fully implement them), or¬†less than 32bits for floats and ints.
    => you may won’t afford a bigger device, but at least be sure to do the updates (drivers, etc).
  • GLSL unrolls most loops and all function calls, so your shader can be extremely longer than you think. This can crash the compiler, timeout it, or request more resource than your GPU can afford.
    => Try to guess the consequences of your coding style.
            Do loop for selecting then treat after, rather than treat or call a function inside loops.
             Fear long nested loops (including the ones in called functions).
             User side: try replacing the loop end value by a shorter value.
  • Some expressions are solved at compilation time rather than at run time (e.g. #define and const expressions), and thus can have different precision or treatment of exceptions. E.g. on some systems the full IEEE is not obeyed at compilation time while it is at run-time: float x=0., y=x/x, z=1./x often behave differently with const float (or using 0. directly).
    The optimizer can also solve or partly solve some more. But optimizers vary a lot with drivers, versions, etc.
  • Compilers are still full of bugs:
    • E.g. complex types like structs, mat, vec¬†might do wrong with const¬†qualifier, or in cond?v1:v2 statements, or in loops without {}.
    • Ternary operator used in vec or switch might give unexpected result or not compile.
    • On windows, some operations may make floor/ceil ignored !
    • Redefining locally a global variable already used in the current function crash the compiler or even the driver on linux.
    • A variable having the name of a function might be not accepted as well on some compilers.
  • => Do the updates.
          РSuppress suspect const qualifier (optimizer is smart enough, anyway)
    – don’t reuse function name for variables
    – don’t reuse global names when both the local and global variables are used in the same block
    ¬† ¬† ¬† –¬†Protect suspect blocks by {} or (). Try replacing by if then else.
  • Some bugs occur at unrolling of long loops or long shaders (e.g. the last instruction of a loop is not always executed, or implicit initialization not always done).
    => Do the updates.
           Rearrange the suspect loop. E.g., move a conditional break as earlier statement.
           Initialize all variables.
            (See also section about long shaders.)
  • The number of simultaneous key accounted in Shadertoy keyboard texture depends on the keys, and probably on the OS. (Web events are just a compatibility mess).
  • There seems to be¬†some GPU-specific¬†and OSX-specific bugs.

There are some pure GLSL bugs (or spec sloppyness)… consistent or not through OSs

Note that often, these correspond to “undefined” in the GLSL spec, even if it is in C/C++ , and some compilers do the reasonable thing. But others “won’t fix” because it’s not in spec.

  • clamp(NaN,0.,1.) should be NaN. It is not. And it is 0 on linux (GLSL), but 1 on windows (Angle).
  • same for smoothstep(NaN) (since it uses clamp)
  • sqrt(v) with v=-1 is NaN… but if v is a const or a #define. Then, it is 0.
  • (-3) % 4 gives 1 on OpenGL and -3 on Windows Angle if -3 is in a non-const variable (otherwise 0).
    While mod(-3.,4.) does gives 1. on all systems (test here).
    [ odd, indeed: on OpengL  (-a)%b is off by 5 if non-const, and always 0 if const. ]
    ‚Üí Special case trick: replace x%2 with x&1 .
    ‚Üí a portable integer modulo that works on negative here.
    Usually only the first parameter can be negative.
    ‚Üí #define imod(a,b) ( a >= 0 ? (a) % (b) : -(a)%(b)==0 ? 0 : b – ( -(a) % (b) ) )
  • uint or uvec(negative) is correct for ints and const floats¬†while wrongly gives 0 for unconst floats.
    Patch:   #define unsigned(v)   ( (v) >= 0. ? uint(v) : -1U-uint(-(v)) )   or:  uint(int(floor(v)))
  • int(x) vs int(floor(x)) on negatives can be equal or OpenGL and different on Windows/Angle.
  • Out of bounds array indexes are clamped on OpenGL, but can cause strange non-local side effects on Windows/Angle.
  • round(x) is often implemented as roundEven(x) rather than floor(x+.5).

Attention: Shadertoy masks the warnings if the compilation is ok.  Maybe it could be good practice to once insert a syntax error just to see the warnings, then inserted in the code and indistinguishable from full errors. Example here : sqrt(-1) were indeed warned as invalid at compilation time.

Classical float “bugs”: (not specific to GLSL)

  • x/x might not be exactly 1., even for int-in-floats (integers up to 16,777,216 are exactly represented by IEEE floats on 32bits.¬†+ – * will be exact… but not the division). This is due to the fact that compilers generally replace division by multiplication with the inverse.
    A consequence is that fract(x/x) might be ~1 instead of 0 (about 10% of times)
  • mod on int-in-floats has some bugs (due to the division as above). e.g. mod(33.,33.) might be 33, some for about 10% of values. ūüė¶ (Note that you can’t verify by testing this on const since it would be resolved at compiler time, not run time).
    => If you really aim at integer operations, emulate a%b with a-a/b*b [webGL2 now have full integer arithmetic: just do it !]
  • A reminder than floats have limited precision and span… and worse for 16bits floats.
    A goodies and a trap are denormalized floats: IEEE provides an extension of the range, at the price of collapsing precision.
    Note that without this extension, 32 floats overflow for exp(83.) (same for sinh,cosh,tanh), giving NaN and thus black in Shadertoy.
  • -0 is not totally equal to +0. If you test equality or order directly you’ll find as expected, but a¬†surprise comes when comparing there inverse. Indeed it’s a IEEE feature. If some rare case, this unforgotten sign can create bugs (or save the day in geometry).
  • IEEE treatment of NaN and INF can be surprising, despite logical. In shadertoy NaN is displayed as contaminating black, while +INF is white and -INF is black.
    But their implementation can also be bugged in some cases, or not implemented at all in low-end GPUs.
  • ints op behave strangely on negatives. Rule of thumb: often doing sign(x)*op(abs(x)). It might be dangerous in conversions, e.g. int(-1.2) = -1¬†. Sometime what you really want is int(floor(x))¬†.


Not all extensions are available on all browser (check here). You can check that an extension is there using #ifdef GL_EXT_shader_texture_lod (for instance).
Alas, all¬†extensions now part of¬†WebGL2 core won’t have the old define set: the extension bag is totally different. You can test webGL version with __VERSION__ ( example here .)

Some more subtle issues:

  • Some browsers seem to decompress R,G,B texture channels¬†on slightly different ways.
  • Shadertoy don’t currently use sRGB textures. You have to ungamma – regamma them yourself, but it means that interpolation and MIPmap are slightly biased (but most shader writers seems to don’t know gamma issues at all, anyway ūüôā ).
  • Sound buffering seems to be done on very different ways depending of the system. No problem with sound playing, but issues start when you inspect inside (e.g. time sync and precise buffering range).
  • A shadertoy can be displayed at very different resolutions, depending on your screen size, window size, and various¬†other factors.¬†The aspect ratio can varies, the size might not even be¬†even. So a special configuration causing a glitch might occurs just at¬†your personal display size.
  • In particular, derivative and MIPmap level evaluation are done within 2×2 pixels blocks. So a very slight shift might make a discontinuity invisible or causing a glitch. This typically occurs when you force fract(uv) or use angles as texture coordinates. Also with derivative of variables that might not be set in the neighbor pixel.
  • The shader¬†looks a lot darker or more saturated for you (or for all others).
    => Ever heard about gamma correction ? ūüôā
    In particular, is your monitor in “multimedia mode”,
    or didn’t you played with the contrast or gamma curve (on monitor or on GPU preferences window) ?
  • Textures, sound, video are loaded asynchronously and can sometime be a few frame late.
    =>  If you precompute data in a Buffer, keep redoing it for a few dozen frames.
            e.g.,  if ( iFrame < 30 ) { init } .

Testing OpenGL vs Angle vs D3D version on Windows

On Windows the browser (at least Chrome and Firefox) can use either true GLSL-ES or transpiling to HLSL via the Angle library provided by Google.
You can switch: (more and updates here )

  • firefox: ¬† URL: ¬†about:config¬† ¬†; search “angle”; click on¬†webgl.disable-angle to switch (immediate effect)
  • chrome: relaunch with chrome.exe --use-gl=desktop (or create an alias).
    [edit]: nowadays, rather do chrome.exe --use-angle=gles   or   =gl

Moreover, Angle use two different versions of D3D on chrome vs firefox, which can thus shows up different bugs and behaviors: in case of doubt, try both.

Remote testing your shadertoy on different browser and OS:

  • browserling.com¬† ¬†( 3′ free trial without registration on all Windows and Android. Unlimited (?) on Windows7 )
  • Free Windows/Linux/MacOS online Emulator: onworks.net, or as Chrome¬†plugin
  • Geekprank XP simulator¬† :-p
  • There are many others with free trial after registration stage, e.g. testingbot
  • [ no WebGL ! ]¬†browsershots.org¬† ( free, all OS/browsers. Unselect all (bottom) and pick only what you need ! )

WebGL2.0 compatibility issues

  • Are you sure your browser is webGL2.0 ? ¬†-> Test here.
  • on Windows, % and %= works even on floats, while the spec specifies it should only be valid for ints. ‚Üí On floats, only use mod.
  • Continuation to next line with \ (e.g. for macros)¬†:
    not accepted initially by Firefox.
    Not accepted by Firefox ESR (new bug ?)
  • Return in divergent branches:
    webGL2 GLSL-ES compilers are new, thus come with new bugs. An old issue came back: when one branch of parallel evaluation has a return while others don’t. The return can then be missed, possibly crashing the compiler/driver via infinite loop, or “just” cause wrong or slow results.
  • If: nVidia (linux+windows) ignores diverging returns in if. Test here.
  • Switch: Windows ignore diverging returns inside switch. Test here¬†,¬†here.
  • Switch: linux refuses unreachable statements, typically:¬†return; break;
  • texture() in non-unrollable loop:
    Windows/Angle tries to do something horrible to¬†guess MIPmap level. In case of non-unrollable loop this generates so much¬†extra code that it can easily overwhelm the compiler. But OpenGL might just get MIPmap totally wrong if pixels aren’t locally coherent, anyway.
    -> use texelFetch or textureLOD instead. Test here.
  • Declarations in for:
    Windows bug if a loop counter is declared in another loop. (for(int i,j;..) for (;j<N;j++) )

Link to all glsl bug related shadertoys.

Usual tricks in Shadertoy / GLSL

Ever figured the ‘?’ icon at the bottom-right of the source area ūüôā ?
And the “shader Inputs>” on top-left ? ūüôā
These are, respectively, a GLSL ES summary, and a list of the Shadertoy variable.

Don’t get hypnotized by names

  • Shadertoy API defines fragCoord and fragColor. so don’t access directly to GLSL gl_FragCoord and gl_FragColor or you might break things some day. Using discard in Buffers might also give surprising side effects.
  • Also, the Image tab is not the true beginning of the GLSL shader, so don’t use pragmas and special declaration meant for the top lines of pure shaders.
  • fragColor is not initialized: be sure to do it, even if it seem to work now and for you. It’s often not the case on other machines or when the shader complexifies.
  • It’s your program, so these names can be changed. I personally prefer U and O, as kind of math notations (and shorter more readable expressions if you are a science nerd like me ūüôā ).
  • These are variable like any others, so you can reuse them: fragCoord /= iResolution.y;  fragColor = pow(fragColor,vec4(1./2.2));

GLSL already knows vectors and matrices

  • comprising operations like length, distance, normalize, dot, cross and mat*vec
  • Many operations directly work on vectors (acting on each components)
  • Some special operations do boolean operations on vectors (all, any, lessthan…)
  • Many operations implicitly expend floats to vectors ( V+2., V/=2., step(0.,V), clamp(V,0.,1.), mix(V0,V1,.2)… )
  • and constructors are also casters ( e.g. V=vec4( x>y , 0 , vec2(x) ) ).
  • In particular, you can do V=vec2(0,0), or even V=vec2(0).
    Note that mat(d) setup a diagonal matrix with d along the diagonal: mat(1) is Identity matrix.
  • GLSL already knows 3D graphics operations such as reflect, refract, faceforward
  • GLSL provides many goodies like clamp, mix (linear interpolation), smoothstep (Hermite weighting+clamp)

NB: Complex calculus easily implements as vector and matrices :

  • Use vec2 for definition,  + – between complexes,  + – * / with a float
  • complex multiplication of z1 by z2 is mat2(z1,-z1.y,z1.x) * z2
  • complex division of z1 by z2 is z1 * mat2(z2,-z2.y,z2.x) / dot(z2,z2)

More about maths & complex operations on GLSL here.

C tricks still good for GLSL perfs and programming ease

  • pow(x,y) is doing exp(y*log(x)): costly, not valid for x<0, not perfect precision.
    • for x^2, do prefer x*x !
    • for 2^x use exp2(x).  the reverse log2 also exist (both in most langages ūüôā ).
  • atan API also includes the 2 parameters version doing atan2(y,x)
  • x = cond ? v1 : v0 can be useful, especially for cascaded small expressions.
  • There should be a law punishing people using if cond then x=true else x=false. Just do directly x= cond ūüėČ
  • macros can be a convenient substitutes for templates (e.g. expressions valid for floats, vec2, vec3, vec4).

Perf  vs  readability tricks

You might like to optimize your codes (more or less deeply). Still, know that:

  • Consts are evaluated at runtime, and optimizer knows how to transform /const by *const, so you can keep code readable at not cost: prefer uv *= sqrt(3.)/2.; to code full of black-magic approximate (or long) constants.
  • Opposed to C/C++, GLSL unrolls all the loops it can (and function calls as well), so loop variables are indeed consts too at the “real” final compilation, and thus many derived expressions (or at least parts of them) as well. E.g. vec2 P = vec2(cos(2.*Pi/3.*i),sin(2.*Pi/3.*i)) in loop i will indeed be evaluated at compilation time.  So some cheesy optimizations loose readability for no perf gain (and sometime even causing perf loss).
  • Personally, I feel bumpy to have to read float(int) casts all over the code. You can harmlessly loop on floats most of the time (excepted when integer precision is really required). You might then even loop from 0 to 1 with small steps rather than using float(i)/float(N) all over the place. The semantic is then closer to continuum geometry or integration (i.e. the loop variable is a segment parameterization).
  • IEEE 32bits floating points can store at most 6-7 digits. So it’s useless (and deceiving) to define Pi = 3.14159265358979323846264338327950288419716939937510582097 .
    Oppositely, Pi = acos(-1.) is not guaranteed to be maximum precision.
    Convertly, most of the time your algorithm does not really requires high precision of Pi. But sometime yes (programmers should know when precision is crucial).
  • You don’t need to heavily explicit  2.0f : 2. is just as good. And constructors are auto-casters, so vec4(0) is ok.

More non-intuitive perf or compiling time (or crash) related to how GLSL compiles are treated here.

Uncomplete integer operations

In webGL1 many integer operations are missing. And anyway sometime it’s simpler to do them on floats then cast (or not). But be careful to precision loss. Still,

  • integers up to 16,777,216 are exactly represented by IEEE floats on 32bits (shown here).
  • + – * will thus be exact… But not the division: x/x might not be exactly 1.
  • exp2 and log2 are directly reading the mantissa and exponent so are lossless
  • In particular, << and >> can be represented by *exp2(n) and *exp2(-n)
  • mod on int-in-floats has precision bugs.
    You can do mod directly on ints with  a % b = a-a/b*b
  • Note that you can loop on floats to avoid loads of casts.

GLSL run pixels in parallel

  • So it can computes derivative of any variable for free ! dFdx, dFdy, fwidth
    (The precision is approximate, though: uncentered finite differences within 2×2 blocks)
  • Think parallel. Doing long initializations and definition of arrays won’t be factored through pixels since the whole shader is called at every pixels. So most of the time you save code, memory, registers, by merging the initialization and action loops.
  • A reminder that local memory and number of registers is an ultra-critical resource on GPU. In particular, arrays eat a lot of resources. Use them when they are *really* the only solution (or in small harmless cases).
  • Think procedural: to draw 1000 objects on screen, don’t draw and clamp all of them in your shader – i.e. full set checked at each pixel. Try to find the one(s) that cover the pixel, then render only this one. In particular, for regular grids use mod/fract rather than loops. Cf example.
  • Texture access are also costly (e.g. in big loops), especially if random access or depending on previous computation.

Texture tricks (GLSL or Shadertoy)

  • MIPmap is simply activated by switching the texture mode. Still,
    • You can bias it (force less or more blur) via a third parameter at texture call.
    • At parameterization discontinuity the automatic estimation on the LOD might be very wrong. => you can force it using texture…LodEXT
    • Note that MIPmap can be used to approximate integrals.
  • texture…gradEXT directly computes the texture derivative
  • Noise color texture: G and A channels are R and B translated by (37.,17.) , if no vflip. This allows to fake interpolated 3D noise.
  • tex15 is an ordered Bayer matrix : first made for easy half-toning (just threshold it with the grey level), it also provides a permutation table in [0,63].
  • Shadertoy buffers can be used to precompute data.
    More generally, in a multi-buffer algorithm if the result of a buffer is not expected to change do the computation only at iFrame==0  (or up to a delay, if asynchroneous data such as images are used).
  • Note that sound texture includes the FFT of the music.
  • Check for the magic keyboard matrix ūüôā

More “touchy” tricks (e.g. for code golfing)

  • The final alpha is ignored, so you can work directly on pixelColor or do vec4(myGreyShader).
  • The final color is naturally clamped (your screen pixel won’t be negative or surbright ūüôā ) so for the final image operation you can forget the last clamp.
    ( Of course this can be wrong for intermediate calculations, and buffers do store unclamped floats. )
  • You must initialize variables, comprising out parameters (such as pixelColor).
    But v -= v will work 99.9999% of the times. The only theoretical issue is when the reused register occurred to value NaN by chance (which I’ve never seen occurring up to now).

Advanced super-tricks

You want to use your own texture ? read section Extending Shadertoy & more.
(But it won’t save and others people won’t see it if they don’t do the same insert.)


See the dedicated section .

Special Shadertoy features


(example here , utils here, many values there, ref shader here )

  • iMouse.xy: last mouse click or current mouse drag position.
    Attention: these are integers, while pixels are between integers.
  • iMouse.zw:
    • >0: ongoing drag; starting drag position.
    • otherwise abs() contains the last drag-start position anyway.
  • mouse button events ( added in Nov, 2020 ):
    • down even (i.e. just clicked) if¬† iMouse.z > 0. && iMouse.w > 0.
    • currently down: if¬† iMouse.z > 0. ( && iMouse.w < 0.)
    • released: if¬† Mouse.z < 0 && iMouse.w < 0.
    • up event: you have to track for 1st release
    • demo: ref here and¬† here, use here.¬†
    • Double-click , up event, and more: here.


( example here, utils here and there , updated ref shader here )

  • special texture keyboard: size 256 x 3 ; ¬† values in .x field.
    • (ascii+.5)/256, 0.5/3. : 1 if key #ascii is currently pressed (keydown) otherwise 0
    • (ascii+.5)/256, 1.5/3. : 1¬†at the moment key is pressed (keypressed) otherwise 0
    • (ascii+.5)/256, 2.5/3. : 1/0 toggle for key #ascii

Note that only 2 states were first implemented; keypressed was added at an undetermined time. But compatibility should be safe if only non-zeroness or > .5 was tested rather than value 1.

A reminder that world-while people don’t have Qwerty keyboards: for games and exploration, please use arrows (possibly in addition to your usual controls).
FYI:  LEFT:37  UP:38  RIGHT:39  DOWN:40
PAGEUP:33  PAGEDOWN:34  END : 35  HOME: 36
Modifiers:  SHIFT: 16  CTRL: 17  ALT: 18


( many values there , ref shader: iTime, iTimeDelta  )

  • float¬†iTime (or iGlobalTime¬†) : seconds(+fracs) since the shader (re)started.
  • vec4 iDate: year-1, month-1, day, seconds(+fracs) since midnight.
  • int iFrame: frames since the shader (re)started.
  • float iTimeDelta: duration since the previous frame.
  • float iFrameRate: average FPS.
  • float iChannelTime[4] : current time in video or sound.


Test current values here.

  • vec3 iResolution: for window and buffers.
  • vec3 iChannelResolution[4] : for texture input ¬†of any kind.
  • ¬†.x,y is width,height.
    • Don’t expect the ratio to be the same on all computers (or even to have width>height, e.g. on smartphones and tables). Indeed it can¬†even change between icon, working view and fullscreen.
    • Don’t expect videos (and textures) to have the same ratio than your window: texture(fragCoord/iResolution.xy) wrap the full image, meaning possible distortions.
    • A reminder that a distortion-free scaling means scaling by a scalar, not a vector. e.g.¬†fragCoord/iResolution.y , not .xy. We often like centered coordinates, with y in range [-1,1]:
      vec2 uv = (2.*fragCoord-iResolution.xy ) / iResolution.y ;
  • .z is the ratio of the pixel¬†shapes. For now it seems to always be 1.

For buffers relying on persistence, they won’t keep valid if resolution changes while running: consider testing change, or a magic key to reinitialize. Similarly if you want to allow fullscreen, let¬†3″ at start for the user can switch ( if (iFrame<200)… )

Buffers A..D

Instead of being displayed (as for buffer “image”), the result is stored in the special texture of same name.

  • At each frame they are evaluated in A..D order, then image.
  • For persistent or incremental effects, make your buffer read the same texture it produces (i.e. previous state vs next state). It seems to be initialized to 0.
  • To use persistence, you might compute something only at frame 0 : if (iFrame==0)
    Still, if it relies on image textures, they need some time to be asynchronously loaded so consider something like if (iFrame<30) { init } else { fragColor = texture(sameBuffer); }
    But this is na√Įve not always sufficient: see better solution here.
  • Note that these textures are floats, not bytes, so not bounded to [0,1]. Itt seems to be full ordinary 32 bits floats on most systems.
  • If you want to use the texture as an array, a reminder that fragCoords are mid-integers ‚Üí consider fragCoord-.5 to get the integer index, or (vec(i,j)+.5)/iResolution.xy to get the texture coordinate.
    Or use texelFetch( ch, ivec2(fragCoord) , 0 ).
  • Attention: MIPmap of buffers is off by default. See flags at texture binding as channelN.


Similar to BuffA, with the following specificities:

  • Result is a cube map, so you indeed render the 6 faces of a cube.
    So instead of a main(out vec4 fragColor, in vec2 fragCoord), you have a more complicated mainCubemap( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir ) (Note that it is similar to the alternate mainVR() ).
    fragCoord and iResolution are relative to each face,
    fragRayOri is vec3(0) ( = cube center )
    fragRayDir is the ray direction hitting the cubemap texel, the same way you use it when fetching a cubemap value.
  • This buffer is always 1024 √ó 1024 √ó 6, whatever the window resolution (which can makes costly life icons).
  • This buffer is stored as half-floats
  • A reminder that texelFetch() don’t work on CubeMaps. ( but textureLod() and MIPmap do ).
  • As for Buffers, MIPmap is off by default. See flags at texture binding as channelN.

Note that it is up to you to recompute the environment at every frame, or only once using if (iFrame==0).
Geeky detail: this is the only power-of-two buffer, meaning here dynamic MIPmap is not bugged. Enjoy ūüėČ

So there are many potential use modalities: E.g.n as a spherical environment (possibly even built by ray-tracing at every frame), as 6 extra buffers (with the lovely properties mentioned above), or even as big bulk storage to encode anything, comprising volumes (e.g. 128³ RGBA interpolated volume, raw ~900³ binary voxels, or volumetric light or  fluid simulation) or video images (another).
Attention: if you just want to use it as “one 1024√ó1024 buffer”, do determine that you are in face #0 or the computation will been uselessly done in the 5 other faces too. E.g.: if ( dir.x > max(abs(dir.y),abs(dir.z)) )
The orientation of each face coordinates is strange, so better see the examples below.


This is a special convenience tab, which content is included in all the other tabs.
Useful to set up only once all your global consts, macros, and utility functions.

Attention: to avoid inter-buffers incoherence, most uniforms ( iChannel*, iResolution,  iTime, etc) are not directly available here (or pass them as function parameters).
But it can still work in macros, as code is inlined only once in Buffers. E.g. #define R iResolution.xy  is accepted ( if no regular function uses it in Common ) when vec2 iResolution.xy  is not.
Now, relying on macros functions instead of regular functions just because you need to use an uniform can be cumbersome. A tricks is then to use a macro just as translator, as here (at end of Common): 
type myfunc(type1 param1,...N, type1' uniform1,...M) { ... }
#define myfunc(param1,...N) myfunc(param1,...N, uniform1,...M)
A reminder that the type of iChannelN is sampler2D  .

Provided textures

Note that most of them (comprising volumes, Buffers and video) can be toggled between nearest / linear / MIPmap interpolation, and clamp / repeat warp flag.

Attention: black&white textures are now encoded as red texture. Use vec4(texture()) or texture().rrrr to get the corresponding RGBA. Note that you can use textureSize() to detected 1-channel textures.
Attention: textures often need a few frame to load, which might spoil custom initialization if done at iFrame==0. -> iChannelResolution[] stick to 0 if the texture is not yet ready (see test). Same for higher LODs in textureSize().
-> Safe texture-load-dependent initialization.

special textures :

  • Keyboard (see above), webcam (video), mic(sound), soundcloud (sound)
  • Buffers A,B,C,D (see above)
  • Noise random texture (see below), Bayer texture (see below)
  • Font texture (see below)

regular textures :

  • Image textures: various colors or grey images of various resolution are provided.
    • some wrap continuously and some not.
    • .a is always 1.
    • Color space is sRGB: no transform required for display, but if you want to process computation on them (physical rendering, image treatment) you should convert them to flat before (pow(img, 2.2) ) and back to sRGB after (pow (img, 1./2.2)). Note that this can be approximated by img^2 and sqrt(img).
  • Videos : time-evolving textures (sound is immediately played; the shader can’t access it).
    • They are rectangular: check iChannelResolution.
    • No wrapping: use fract if required.
    • No MIP-mapping.
  • Nyan cat: 256 x 32. Stores a 8 frame cartoon animation. .a is defined.
  • Cubemaps: encode environment as 6 maps. to be used with textureCube()
  • Audio textures: (see below)

Noise textures

6 textures of random uniform values are provided: two 2D low-res, two 2D high-res, one 2D bluenoise, two 3D textures; 3 grey and 3 rgba.

  • 2D Color texture: G and A channels are R and B translated by (37.,17.) , if no vflip. This allows to fake interpolated 3D noise texture. Ref example here.
  • 2D blue-noise texture, i.e., with values “well distributed” rather than pure random (i.e., white noise). Examples of use¬†here.
  • 3D textures. Ref example here.
  • A reminder that the uniform random value is at pixel centers, that are between-integer coordinates. Other uv values can be useful for nice interpolation, but are no longer uniform and lesser std-dev).
  • These textures are in flat colorspace: ready to use for computation, but gamma/sRGB conversion required for faithful display.

Procedural noise (ex, with base noise=texture):
– ref shader here.
– see header comments for all the variants: classical vs simplex noise, value vs gradient noise, etc.

Bayer texture

tex15 is an ordered Bayer matrix :

  • It allows easy dithering and half-toning (just threshold it with the grey level), example here.
  • It also provides a permutation table in [0,63], example here.
  • This texture is in¬†flat colorspace: ready to use for computation, but gamma/sRGB conversion required for faithful display.

Font texture

This special texture encodes 256  characters in tiles.

  • It contains 16×16 tiles of 64×64 pixels.
    • int¬†c=#ascii ¬† is found at vec2( c % 16, 15-c / 16 )/16.
    • a reminder that #ascii = 64+#letter and lowercase = 64+32+#letter
  • .x provides an anti-aliased mask of the characters.
  • .w gives the signed distance to the character border.
  • .yz gives the distance gradient.

More details here. Example & utils here and here and here and there.

Sound in (audio textures)

A provided music or a user-chosen music from SoundCloud can be used as texture.

  • Texture size is bufferSize x 2.¬†(e.g., ¬†512 x 2).
  • index, .75 : music samples
    • It is¬†a buffer refreshed along time, but the precise working seems very system dependent plus the synchronization with image frames is not guaranteed. So drawing the full soundwave or using a naive sound encoding of image won’t be faithful (or possibly only for you).
  • index, .25: FFT of the buffer
    • x / bufferSize = f / (iSampleRate /4.) ¬† , example here.
      iSampleRate give the sampling rate, but it seems incorrectly initialized on many systems: if precision is important, try manually 44100 or 48000.

Ref shaders: music, soundCloud, microphone.

Sound out (sound buffer)

Instead of being displayed (as for buffer “image”), the result is stored in¬†a audio¬†buffer.

  • in: you have the choice between indexing your sound formulas with time or sample id. Time is easiest, but some precision is lost after a few dozen seconds (sometime noticeable in some sounds). Method to use sample id: here.
  • out: x,y channels correspond to left and right audio stereo. FragCoord corresponds to the time sample. ( iSampleRate give the sampling rate, but it seems incorrectly initialized on many systems) .
  • This buffer is evaluated once before the image shader run, then the music is played statically. So there is currently no way to interact between image and sound shaders. One can’t even read textures or access image related Uniforms parameters.

More here.


If your system can display stereo, you can compute stereo shaders.
Just implement the additional adapted mainImage variant:
mainVR( fragColor, fragCoord, fragRayOrigin, fragRayDir )

More here.


This debug helper has been added in October 2020.
st_assert( cond [, alertId ] ) displays a color corresponding to alertId 0,1,2,3 if cond fails (default Id = 0).
NB: Alert Id 1 wins other alert 0, etc.
Two typical uses:
– make pixel red where expression(uv) reaches an invalid range
– make some red region when a uv-independent value reaches an invalid range.

Attention: it is only allowed in Image Buffer.

Reference example here. Utils to test several expressions here.
Since it allows to display colors even when visiting a deep function, it can also be twisted to display variables as viewmeters or numbers.

A propos

Shadertoy is one of the online tool letting you play interactively with GLSL ES shaders (and browse other’s shaders). But soon, several issues will appear:

  • How to use Shadertoy features (are you sure to really know them ? ūüôā )‚Üí see here.
  • GLSL is not C, GLSL ES is not GLSL. How to program, and how to program well, on this calculus model ? ‚Üí see here.
  • As a quite unmature technology, there are many compatibility pitfall ūüė¶ . What are they; how to avoid them ?‚Üí see here.
  • Shadertoy has some specificities. How ¬†to exploit them at¬†best ?‚Üí see here.
  • Shadertoy is now WebGL 2.0. What’s new ? ¬†‚Üísee here.

In this blog, we will explore some of these questions.

Disclaimer:¬†I’m not related to the development team. I’m just a hard user of the tool ūüôā