Blue Ocean Lines
to generate a new and unique variation
Every variart piece is open source, you can see exactly how it is drawn from the code.
const numberOfWaves = 20;
const baseLineSpacing = 20;
const maxAmplitude = 50;
const minAmplitude = 4;
const width = 500;
const height = 500;
const strokeWidth = 2;
s.rect(0, 0, width, height).attr({
fill: "#0a1930"
});
// Create multiple wave lines
for (let i = 0; i < numberOfWaves; i++) {
createSmoothWaveLine(i);
}
function createSmoothWaveLine(index) {
// Randomize parameters for each line
const segmentCount = 30 + Math.floor(Math.random() * 15); // More segments for smoother lines
const frequency1 = 0.8 + Math.random() * 2.2; // Slightly reduced frequency range for smoother waves
const frequency2 = 0.6 + Math.random() * 1.4;
const phaseShift = Math.random() * Math.PI * 2;
const verticalShift = (Math.random() * 30) - 15;
// Calculate amplitude based on position in the stack
const normalizedIndex = index / numberOfWaves;
const amplitudeFactor = 1 - 2 * Math.abs(normalizedIndex - 0.5);
const amplitude = minAmplitude + (maxAmplitude * amplitudeFactor);
// Create wave path using cubic Bezier curves for smoother appearance
let points = [];
// Generate points for the wave with higher resolution
for (let i = 0; i <= segmentCount; i++) {
const x = (width * i) / segmentCount;
const progress = i / segmentCount;
// Calculate fade factor (fade at both ends)
const fadeFactor = Math.sin(progress * Math.PI);
// Create smoother wave pattern with multiple sine waves
const y = (height / 2) +
Math.sin(progress * Math.PI * frequency1 + phaseShift) * amplitude * fadeFactor *
(0.5 + 0.5 * Math.sin(progress * Math.PI)) +
Math.sin(progress * Math.PI * frequency2 + phaseShift * 1.5) * (amplitude / 2) * fadeFactor +
verticalShift;
points.push({x, y});
}
// Create smooth path with curves - using tension parameter for smoother curves
let pathString = `M${points[0].x},${points[0].y}`;
// Use a tension factor to make curves smoother
const tension = 0.33; // Lower values create smoother curves
for (let i = 1; i < points.length; i++) {
const cp1x = points[i-1].x + (points[i].x - points[i-1].x) * tension;
const cp1y = points[i-1].y + (points[i].y - points[i-1].y) * tension;
const cp2x = points[i].x - (points[i].x - points[i-1].x) * tension;
const cp2y = points[i].y - (points[i].y - points[i-1].y) * tension;
pathString += ` C${cp1x},${cp1y} ${cp2x},${cp2y} ${points[i].x},${points[i].y}`;
}
// Create the path
const wavePath = s.path(pathString);
// Randomize line spacing slightly
const lineSpacing = baseLineSpacing * (0.85 + Math.random() * 0.3);
// Calculate opacity with fade effect based on position
// Lines at the edges are more transparent
const edgeFadeFactor = 0.4 + (0.6 * Math.sin(normalizedIndex * Math.PI));
const opacity = (0.5 + Math.random() * 0.3) * edgeFadeFactor;
// Style the path
wavePath.attr({
fill: "none",
stroke: "#00a8ff",
strokeWidth: strokeWidth,
strokeOpacity: opacity,
transform: `translate(0, ${(index - numberOfWaves/2) * lineSpacing})`
});
}
Copyright © 2014–2025 Kevin Marsh. All rights reserved. Questions? Comments? hello@variart.io