COLORMIND - THREEJS PROCEDURAL ANIMATION SOURCE CODE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ColorMind APi</title>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
font-family: "Inter", system-ui, sans-serif;
line-height: 1.6;
}
body {
background: #000000;
color: #ffffff;
min-height: 100vh;
padding-bottom: 2rem;
}
.blog-content {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.page-header {
margin-bottom: 2rem;
padding: 1rem 0;
text-align: left;
}
.page-header .tagline {
color: #ffffff;
font-size: clamp(1.4rem, 2vw, 1.8rem);
opacity: 0.9;
font-weight: 400;
margin-top: 0.5rem;
}
.intro-text {
text-align: left;
margin-bottom: 2rem;
max-width: 800px;
}
.intro-text p {
margin-bottom: 1rem;
font-size: 1.1rem;
opacity: 0.8;
line-height: 1.7;
}
.intro-text a {
color: #ffffff;
text-decoration: none;
border-bottom: 1px solid #666;
transition: all 0.3s ease;
}
.intro-text a:hover {
border-bottom: 1px solid #ffffff;
}
.controls-container {
display: flex;
justify-content: flex-start;
gap: 1rem;
margin-bottom: 2rem;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.button-group {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
padding: 0.85rem 1.75rem;
font-size: 0.95rem;
border-radius: 8px;
background: #000000;
color: #ffffff;
border: 1px solid #ffffff;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
font-family: "Inter", system-ui;
font-weight: 500;
letter-spacing: 0.5px;
position: relative;
overflow: hidden;
min-width: 180px;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn:hover::before {
width: 300px;
height: 300px;
}
.btn:hover {
background: #ffffff;
color: #000000;
border-color: #ffffff;
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(255, 255, 255, 0.1);
}
.btn:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
}
.btn:disabled:hover::before {
width: 0;
height: 0;
}
.btn:disabled:hover {
background: #000000;
color: #ffffff;
border-color: #ffffff;
box-shadow: none;
}
.btn-secondary {
background: #000000;
border: 1px solid #666;
}
.btn-secondary:hover {
background: #333;
color: #ffffff;
border-color: #666;
}
.palette-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.color-card {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
opacity: 0;
transform: translateY(30px);
}
.color-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
border-color: rgba(255, 255, 255, 0.2);
}
.color-swatch {
height: 150px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
cursor: pointer;
transition: transform 0.2s ease;
}
.color-value {
background-color: rgba(0, 0, 0, 0.8);
padding: 10px 18px;
border-radius: 6px;
font-weight: 500;
font-family: 'Monaco', 'Consolas', monospace;
user-select: all;
color: white;
font-size: 0.9rem;
border: 1px solid rgba(255, 255, 255, 0.1);
letter-spacing: 0.5px;
}
.color-info {
padding: 1.2rem;
background: transparent;
}
.color-name {
font-weight: 600;
margin-bottom: 8px;
color: #ffffff;
font-size: 1.1rem;
}
.color-role {
font-size: 0.85rem;
color: #aaa;
letter-spacing: 0.3px;
}
.notification {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
color: #000000;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
transform: translateY(100px);
opacity: 0;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
border: 1px solid rgba(0, 0, 0, 0.1);
z-index: 1000;
font-weight: 500;
}
.notification.show {
transform: translateY(0);
opacity: 1;
}
.loader {
display: none;
text-align: left;
margin: 30px 0;
}
.loader.show {
display: block;
}
.spinner {
border: 3px solid rgba(255, 255, 255, 0.1);
border-left-color: #ffffff;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 0 1rem 0;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
footer {
text-align: left;
margin-top: 40px;
padding: 20px 0;
color: #888;
font-size: 0.9rem;
}
footer a {
color: #ffffff;
text-decoration: none;
border-bottom: 1px solid #666;
transition: all 0.3s ease;
}
footer a:hover {
border-bottom: 1px solid #ffffff;
}
@media (max-width: 768px) {
.palette-container {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
}
.blog-content {
padding: 1.5rem;
}
.controls-container {
margin-bottom: 1.5rem;
padding: 1rem;
}
.button-group {
flex-direction: column;
width: 100%;
}
.btn {
min-width: 100%;
}
}
@media (max-width: 480px) {
.blog-content {
padding: 1rem;
}
.page-header .tagline {
font-size: 1.3rem;
}
.color-card {
border-radius: 10px;
}
}
</style>
</head>
<body>
<div class="blog-content">
<header class="page-header">
<p class="tagline">Create random palettes with the ColorMind API. Click on any colour to copy its hex value.</p>
</header>
<div class="intro-text">
<p>This tool generates harmonious color palettes using artificial intelligence. Each palette consists of five complementary colors suitable for design systems, UI projects, and branding.</p>
<p>Click "Generate Palette" for AI-curated colors or "Random Colors" for completely random combinations.</p>
</div>
<div class="controls-container">
<div class="button-group">
<button id="generateBtn" class="btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 6V3L8 7L12 11V8C15.31 8 18 10.69 18 14C18 17.31 15.31 20 12 20C8.69 20 6 17.31 6 14H4C4 18.42 7.58 22 12 22C16.42 22 20 18.42 20 14C20 9.58 16.42 6 12 6Z" fill="currentColor" />
</svg>
Generate Palette
</button>
<button id="randomBtn" class="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.59 9.17L5.41 4L4 5.41L9.17 10.62L10.59 9.17ZM14.5 4L16.54 6.04L4 18.59L5.41 20L17.96 7.46L20 9.5V4H14.5ZM14.83 13.41L13.42 14.82L16.55 17.95L14.5 20H20V14.5L17.96 16.54L14.83 13.41Z" fill="currentColor" />
</svg>
Random Colors
</button>
</div>
</div>
<div class="loader" id="loader">
<div class="spinner"></div>
<p>Generating your palette...</p>
</div>
<div class="palette-container" id="paletteContainer">
</div>
<footer>
<p>Powered by the <a href="http://colormind.io/api-access/" target="_blank">Colormind API</a></p>
</footer>
</div>
<div class="notification" id="notification">Color copied to clipboard!</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const paletteContainer = document.getElementById("paletteContainer");
const generateBtn = document.getElementById("generateBtn");
const randomBtn = document.getElementById("randomBtn");
const loader = document.getElementById("loader");
const notification = document.getElementById("notification");
const colorRoles = ["Primary", "Secondary", "Accent", "Background", "Text"];
generatePalette();
generateBtn.addEventListener("click", generatePalette);
randomBtn.addEventListener("click", generateRandomPalette);
async function generatePalette() {
loader.classList.add("show");
generateBtn.disabled = true;
randomBtn.disabled = true;
try {
const response = await fetch("http://colormind.io/api/", {
method: "POST",
body: JSON.stringify({
model: "default"
})
});
if (!response.ok) {
throw new Error("Failed to fetch color palette");
}
const data = await response.json();
displayPalette(data.result);
} catch (error) {
console.error("Error fetching palette:", error);
const fallbackPalette = [
[255, 105, 54],
[59, 29, 34],
[39, 22, 30],
[38, 14, 14],
[233, 233, 240]
];
displayPalette(fallbackPalette);
} finally {
loader.classList.remove("show");
generateBtn.disabled = false;
randomBtn.disabled = false;
}
}
function generateRandomPalette() {
const randomPalette = [];
for (let i = 0; i < 5; i++) {
const randomColor = [
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
];
randomPalette.push(randomColor);
}
displayPalette(randomPalette);
}
function displayPalette(colors) {
paletteContainer.innerHTML = "";
colors.forEach((color, index) => {
const hexColor = rgbToHex(color[0], color[1], color[2]);
const colorCard = document.createElement("div");
colorCard.className = "color-card";
colorCard.style.opacity = "0";
colorCard.style.transform = "translateY(30px)";
colorCard.innerHTML = \`
<div class="color-swatch" style="background-color: \${hexColor};" data-hex="\${hexColor}">
<span class="color-value">\${hexColor}</span>
</div>
<div class="color-info">
<div class="color-name">\${colorRoles[index]}</div>
<div class="color-role">RGB: \${color[0]}, \${color[1]}, \${color[2]}</div>
</div>
\`;
const colorSwatch = colorCard.querySelector(".color-swatch");
colorSwatch.addEventListener("click", function () {
copyToClipboard(hexColor);
showNotification();
colorSwatch.style.transform = "scale(0.95)";
setTimeout(() => {
colorSwatch.style.transform = "scale(1)";
}, 150);
});
paletteContainer.appendChild(colorCard);
setTimeout(() => {
colorCard.style.transition =
"all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)";
colorCard.style.opacity = "1";
colorCard.style.transform = "translateY(0)";
}, index * 100);
});
}
function rgbToHex(r, g, b) {
return (
"#" +
((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()
);
}
function copyToClipboard(text) {
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
}
function showNotification() {
notification.classList.add("show");
setTimeout(() => {
notification.classList.remove("show");
}, 2000);
}
});
</script>
</body>
</html>