These built-ins take radians and work per component on float and vec2 to vec4: radians(), degrees(), sin(), cos(), tan(), asin(), acos(), atan() and, new in GLSL ES 3.00, sinh(), cosh(), tanh() and their inverses. atan(y, x) with two arguments returns the full-circle angle of a direction. The exponential group is pow(), exp(), log(), exp2(), log2(), sqrt() and inversesqrt(), the fast 1/sqrt(x) behind normalize():
show('float a = radians(180.0); result = vec4(a, degrees(a), sin(a / 2.0), atan(1.0, -1.0));');
show('result = vec4(pow(2.0, 10.0), exp(1.0), log2(1024.0), inversesqrt(4.0));');
show('result = vec4(sinh(1.0), tanh(10.0), sin(vec2(0.0, radians(90.0))));');3.141593, 180, 1, 2.356194 1024, 2.718282, 10, 0.5 1.175201, 1, 0, 1
atan(1.0, -1.0) is 3π/4, an angle in the upper-left quadrant that the one-argument atan(y / x) could not tell apart from the lower-right one. The last call shows per-component evaluation: sin() of a vec2 returns a vec2, which fills the final two slots. The specification leaves the precision of the trigonometric functions undefined and pow(x, y) undefined for x < 0, so keep angles small and bases positive when results must match across GPUs. Directional and Point Lighting uses pow() for specular highlights.
<!doctype html>
<style>
body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
canvas { display: block; width: 100%; max-width: 600px; }
.names { display: flex; max-width: 600px; text-align: center; font-family: monospace; font-size: 11px; }
.names div { flex: 1; padding: 4px 2px; }
</style>
<canvas id="view" width="600" height="150"></canvas>
<div class="names">
<div>atan(y, x) as hue</div><div>sin(x), cos(x)</div><div>pow(x, 0.5 | 1 | 2 | 4)</div><div>tanh(x), sinh(x)</div>
</div>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
void main() { gl_Position = vec4(gl_VertexID == 1 ? 3.0 : -1.0, gl_VertexID == 2 ? 3.0 : -1.0, 0.0, 1.0); }`;
const fs = `#version 300 es
precision highp float;
uniform float uTime;
out vec4 outColor;
const float PI = 3.14159265;
// 1 near the curve y = f, using screen-space derivatives for an even line width.
float curve(float y, float f) { return 1.0 - smoothstep(0.0, 1.5, abs(y - f) / fwidth(y - f)); }
void main() {
int tile = int(gl_FragCoord.x / 150.0);
vec2 uv = vec2(mod(gl_FragCoord.x, 150.0), gl_FragCoord.y) / vec2(140.0, 150.0); // 0..1
vec3 color = vec3(0.97, 0.96, 0.93);
if (uv.x > 1.0) { outColor = vec4(color, 1.0); return; }
vec2 p = uv * 2.0 - 1.0; // -1..1
if (tile == 0) {
float angle = atan(p.y, p.x) + uTime; // full circle: -PI..PI
vec3 hue = 0.5 + 0.5 * cos(angle + vec3(0.0, 2.1, 4.2)); // cos() works per component
color = length(p) < 0.9 ? mix(color, hue, smoothstep(0.2, 0.25, length(p))) : color;
} else if (tile == 1) {
float x = p.x * PI + uTime;
color = mix(color, vec3(0.12, 0.37, 0.55), curve(p.y, 0.8 * sin(x)));
color = mix(color, vec3(0.71, 0.27, 0.18), curve(p.y, 0.8 * cos(x)));
} else if (tile == 2) {
const float k[4] = float[](0.5, 1.0, 2.0, 4.0);
for (int i = 0; i < 4; i++)
color = mix(color, vec3(0.2 * float(i), 0.4, 0.6 - 0.12 * float(i)), curve(uv.y, pow(uv.x, k[i])));
} else {
float x = p.x * 3.0;
color = mix(color, vec3(0.25, 0.49, 0.23), curve(p.y, tanh(x)));
color = mix(color, vec3(0.88, 0.6, 0.06), curve(p.y, sinh(x) / 10.0));
}
if (abs(p.x) < 0.006 || abs(p.y) < 0.008) color *= 0.85; // axes
outColor = vec4(color, 1.0);
}`;
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, vs], [gl.FRAGMENT_SHADER, fs]]) {
const s = gl.createShader(type);
gl.shaderSource(s, src); gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());
const uTime = gl.getUniformLocation(program, 'uTime');
function frame(ms) {
gl.uniform1f(uTime, ms * 0.001);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>