A dynamic sea wave effect with noise-based organic patterns with animated sine and cosine waves
Creating a realistic and mesmerizing sea wave effect is a popular challenge in creative coding, especially when using GLSL (OpenGL Shading…
A dynamic sea wave effect with noise-based organic patterns with animated sine and cosine waves
Creating a realistic and mesmerizing sea wave effect is a popular challenge in creative coding, especially when using GLSL (OpenGL Shading Language) for WebGL projects. By combining noise-based organic patterns with animated sine and cosine waves, we can simulate the natural motion of water in a visually engaging way.

A dynamic sea wave effect with noise-based organic patterns with animated sine and cosine waves
This approach allows for dynamic, real-time rendering of waves that appear to ripple, swell, and flow across the screen, adding a touch of realism and depth to digital art.
In this tutorial, we’ll dive into the process of creating a sea wave effect with GLSL that blends smooth noise functions with oscillating sine and cosine wave patterns. We’ll walk through how to set up a fragment shader that renders these effects directly in the browser, producing an animated, interactive canvas filled with organically moving waves. Whether you’re new to shaders or looking to expand your creative coding skills, this project will give you the foundation to craft stunning, fluid animations right in the browser. Let’s get started!
To get started, we’ll need an HTML file with a <canvas> element for rendering, a CSS file for styling, and a JavaScript file to manage WebGL and the GLSL shaders. Let’s look at each file in turn.
Project Structure
project/
│
├── index.html
├── styles.css
└── wave.js
The HTML file defines the basic structure of our webpage. We include a <canvas> element for WebGL rendering and link to the CSS and JavaScript files.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GLSL Sea Wave Effect</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<canvas id="glCanvas"></canvas>
<script src="wave.js"></script>
</body>
</html>
The CSS ensures the <canvas> fills the screen without stretching, providing a responsive and immersive experience. css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
Creating a Sea Wave Effect with GLSL
Now, let’s dive into the heart of our project, the wave.js file. This JavaScript file initializes WebGL, compiles the shaders, and creates an animated wave effect using fragment shaders.
wave.js Below is the code for wave.js that generates a dynamic sea wave effect with GLSL:
const canvas = document.getElementById('glCanvas');
let gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
if (!gl) {
alert('WebGL is not supported by your browser.');
} else {
console.log('WebGL context successfully created.');
}
// Vertex Shader
const vertexShaderSource = `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}
`;
// Fragment Shader (Sea Wave Effect with Organic Pattern)
const fragmentShaderSource = `
precision mediump float;
uniform float u_time;
uniform vec2 u_resolution;
// Noise function for organic wave pattern
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// Wave function with increased frequency
float waves(vec2 uv) {
float wave1 = sin(uv.x * 12.0 + u_time) * 0.5;
float wave2 = cos(uv.y * 18.0 + u_time * 0.5) * 0.3;
return wave1 + wave2;
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
uv = uv * 2.0 - 1.0;
uv.x *= u_resolution.x / u_resolution.y;
float n = noise(uv * 10.0 + u_time * 0.2);
float wavePattern = waves(uv);
// Control wave border and color gradient
float border = smoothstep(0.08, 0.1, abs(n + wavePattern));
vec3 color1 = vec3(0.16, 0.62, 0.63);
vec3 color2 = vec3(0.16, 0.57, 0.63);
vec3 color3 = vec3(0.16, 0.48, 0.63);
vec3 color;
if (border < 0.4) {
color = mix(color1, color2, border / 0.4);
} else {
color = mix(color2, color3, (border - 0.4) / 0.6);
}
gl_FragColor = vec4(color, 1.0);
}
`;
// Adjust canvas size dynamically
function resizeCanvas() {
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
About the Fragment Shader (Sea Wave Effect with Organic Pattern), this GLSL code creates a fragment shader to generate a dynamic, organic sea wave effect with color gradients.
Let’s go through it step-by-step to understand how it works.
uniform float u_time;
uniform vec2 u_resolution;
u_time: Tracks the elapsed time, allowing the waves to animate over time. u_resolution: Holds the dimensions of the screen (or canvas), used to make sure the shader scales correctly across different screen sizes.
Noise Function for Organic Patterns
The shader uses a custom noise() function that introduces random variations to create an organic, wave-like appearance.
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
This function creates pseudorandom noise by calculating a dot product of the input coordinates st with a constant vector, feeding the result to sin(), and then taking the fractional part with fract(). It outputs a value between 0 and 1.
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
Wave Pattern Creation
float waves(vec2 uv) {
float wave1 = sin(uv.x * 12.0 + u_time) * 0.5;
float wave2 = cos(uv.y * 18.0 + u_time * 0.5) * 0.3;
return wave1 + wave2;
}
This waves() function creates oscillating patterns to simulate waves. wave1 generates a sine wave along the x-axis with a frequency of 12.0, adjusted over time by u_time. wave2 generates a cosine wave along the y-axis with a frequency of 18.0, moving at half the speed.
These two waves combine to create a complex wave pattern in the final effect.
Fragment Shader Main Logic
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
uv = uv * 2.0 - 1.0;
uv.x *= u_resolution.x / u_resolution.y;
The uv coordinates are normalized to [-1, 1], making the center of the canvas (0,0).
The x-coordinates are scaled by the aspect ratio, ensuring the wave effect scales properly on different screens.
Generate Wave Pattern with Noise
float n = noise(uv * 10.0 + u_time * 0.2);
float wavePattern = waves(uv);
n is a noise value adjusted with a higher frequency (uv 10.0) and moves over time (u_time 0.2).
wavePattern is the result of waves(uv) and is combined with n to add organic variations.
float border = smoothstep(0.08, 0.1, abs(n + wavePattern));
border determines the intensity of the wave pattern, controlled by a smoothstep() function for a gradual transition, providing a smooth, anti-aliased edge for the waves.
vec3 color1 = vec3(0.16, 0.62, 0.63);
vec3 color2 = vec3(0.16, 0.57, 0.63);
vec3 color3 = vec3(0.16, 0.48, 0.63);
vec3 color;
if (border < 0.4) {
color = mix(color1, color2, border / 0.4);
} else {
color = mix(color2, color3, (border - 0.4) / 0.6);
}
gl_FragColor = vec4(color, 1.0);
}
The shader applies a color gradient using three shades (color1, color2, color3). The gradient interpolates based on the border value:
When border is below 0.4, it blends between color1 and color2. When border is above 0.4, it blends between color2 and color3.
Finally, the color is output to gl_FragColor to render the pixel.
This shader combines noise-based organic patterns with animated sine and cosine waves to create a dynamic sea wave effect with a gradient color scheme. The result is a visually appealing wave effect that appears to move naturally over time, with a smooth color transition.
Understanding the Shader Code
- Noise Function: The noise() function generates a random organic pattern, adding texture to the wave effect.
- Wave Function: This uses sin() and cos() to create wave oscillations, simulating the motion of water with controlled frequency.
- Color Gradient: We apply a color gradient with three colors to enhance depth and realism.
Summary
Using GLSL for creative coding unlocks limitless potential for real-time, interactive visuals. This sea wave effect is just the beginning. By experimenting with noise, frequencies, and color gradients, you can craft immersive visuals and animations right in the browser.
메타데이터
- post_id
- ffd56b67dc6c
- slug
- a-dynamic-sea-wave-effect-with-noise-based-organic-patterns-with-animated-sine-and-cosine-waves-ffd56b67dc6c
- url
- https://medium.com/@banyapon/a-dynamic-sea-wave-effect-with-noise-based-organic-patterns-with-animated-sine-and-cosine-waves-ffd56b67dc6c
- canonical_url
- https://medium.com/@banyapon/a-dynamic-sea-wave-effect-with-noise-based-organic-patterns-with-animated-sine-and-cosine-waves-ffd56b67dc6c
- author_url
- https://medium.com/@banyapon
- status
- ok
- fetched_at
- 2026-08-22 07:22:41