Skip to content

Instantly share code, notes, and snippets.

@eightants
Last active June 27, 2023 02:36
Show Gist options
  • Save eightants/6286d8a69cccf9304871c867a9144e19 to your computer and use it in GitHub Desktop.
Save eightants/6286d8a69cccf9304871c867a9144e19 to your computer and use it in GitHub Desktop.
Spotify Duotone Filter with HTML Canvas and Javascript
// Spotify Duotone Filter with HTML Canvas and Javascript
// by Anthony Teo
/*
This function generates a duotone version of an image on an HTML Canvas element using Javascript.
It makes use of the newer canvas functions, including filter and the globalCompositeOperations, making it
less code-heavy compared to existing implementations in canvas.
The benefit of using this method compared to CSS filters is the ability to convert the canvas to an image to be saved.
Libraries like HTMLtoCanvas state in their documentation that CSS filters are not supported, so it is probably not possible
to do so in CSS. This is a purely JS and Canvas implementation.
*/
function Duotone(id, src, primaryClr, secondaryClr, actions = (ctx) => null) {
let canvas = document.getElementById(id);
let ctx = canvas.getContext("2d");
let downloadedImg = new Image();
downloadedImg.crossOrigin = ""; // to allow us to manipulate the image without tainting canvas
downloadedImg.onload = function () {
ctx.drawImage(downloadedImg, 0, 0, canvas.width, canvas.height); // draws image to canvas on load
// Converts to grayscale by averaging the values of each pixel
imageData = ctx.getImageData(0, 0, 800, 800);
const pixels = imageData.data;
for (let i = 0; i < pixels.length; i += 4) {
const red = pixels[i];
const green = pixels[i + 1];
const blue = pixels[i + 2];
// Using relative luminance to convert to grayscale
const avg = Math.round((0.299 * red + 0.587 * green + 0.114 * blue) * 1);
pixels[i] = avg;
pixels[i + 1] = avg;
pixels[i + 2] = avg;
}
// Puts the grayscaled image data back into the canvas
ctx.putImageData(imageData, 0, 0);
// puts the duotone image into canvas with multiply and lighten
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = primaryClr; // colour for highlights
ctx.fillRect(0, 0, canvas.width, canvas.height);
// lighten
ctx.globalCompositeOperation = "lighten";
ctx.fillStyle = secondaryClr; // colour for shadows
ctx.fillRect(0, 0, canvas.width, canvas.height);
// calls any other draws that you want through the function parameter passed in
actions(ctx);
};
downloadedImg.src = src; // source for the image
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>Spotify Duotone Filter with HTML Canvas and Javascript</title>
<script type="text/javascript" src="Duotone.js"></script>
<style>
body {
margin: 0;
font-size: 16px;
font-family: "Helvetica Neue", Helvetica;
}
#main {
width: 600px;
margin: 50px auto;
text-align: center;
}
p {
text-align: left;
margin: 40px;
}
button {
font-weight: 600;
margin: 20px 10px;
padding: 15px 30px;
border: 2px solid black;
background-color: white;
border-radius: 10px;
color: black;
transition: all 0.3s ease;
text-transform: uppercase;
}
button:hover {
cursor: pointer;
background-color: black;
color: white;
}
</style>
</head>
<body>
<div id="main">
<p>This is the canvas generated with the Duotone function. Use the "Convert to Image" button to convert the canvas into a png image below, which can be saved on desktop and mobile.</p>
<canvas id="duotone" width="600" height="600"></canvas>
<button onclick="randomizeColors()">Change Colours</button>
<button onclick="downloadImage()">Convert to Image</button>
<img id="image">
</div>
<script type="text/javascript" src="scripts.js"></script>
</body>
</html>
// Script to use the Duotone function
function randomIndex() {
return Math.floor(Math.random() * 3);
}
function drawText(ctx) {
// Draws text on top of the duotone image
ctx.fillStyle = "#FFFFFF";
ctx.font = "bold 96px Arial";
ctx.textBaseline = "bottom";
ctx.fillText("Avicii", 100, 480); // draws text
ctx.fillRect(0, 430, 80, 12); // draws the line
ctx.textBaseline = "top";
}
function randomizeColors() {
// Arrays of colors, with a random index chosen when clicked.
let primary = ["#f65e35", "#00ff36", "#77acd4"];
let secondary = ["#1e3265", "#23278a", "#033dc5"];
let ind = randomIndex();
// Duotone called with the appropriate arguments
Duotone(
"duotone",
"https://i.imgur.com/WQ1Iydl.jpeg",
primary[ind],
secondary[ind],
drawText
);
}
function downloadImage() {
// function to convert the canvas into a png image and set it as the src of the img tag
document.querySelector("#image").src = document
.querySelector("#duotone")
.toDataURL("image/png");
}
// initial call
randomizeColors();
@arthurfanti
Copy link

That's pretty cool! 👍

@Hg6f9
Copy link

Hg6f9 commented Jun 27, 2023

Hello my friend today was my birthday

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment