Bring your name to life with the ColorCove Custom Name Coloring Page Generator ! Whether you are looking for classic bubble letters, spooky Halloween styles, or elegant fonts, our free tool lets you design custom, printable art instantly.
No sign-ups, no software downloads, and no hidden feesโjust type your text, customize your design, and hit print!
@import url(‘https://fonts.googleapis.com/css2?family=Alfa+Slab+One&family=Bangers&family=Bowlby+One+SC&family=Creepster&family=Fredoka:wght@700&family=Lilita+One&family=Londrina+Solid:wght@900&family=Luckiest+Guy&family=Modak&family=Rubik+Doodle+Shadow&family=Rye&family=Shrikhand&family=Sigmar+One&family=Spicy+Rice&family=Titan+One&display=swap’);
.coloring-generator {
max-width: 1200px;
margin: 0 auto;
font-family: sans-serif;
display: grid;
grid-template-columns: 1fr;
gap: 30px;
}
@media (min-width: 850px) {
.coloring-generator {
grid-template-columns: 320px 1fr;
align-items: start;
}
}
.controls {
display: flex;
flex-direction: column;
gap: 15px;
background: #f1f1f1;
padding: 24px;
border-radius: 12px;
order: 2;
}
.control-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.control-group label {
font-weight: 600;
font-size: 14px;
color: #444;
margin-left: 2px;
}
.controls input, .controls select {
padding: 12px 10px;
font-size: 16px;
border-radius: 6px;
border: 1px solid #ccc;
width: 100%;
box-sizing: border-box;
}
.controls button {
justify-content: center;
padding: 16px !important;
font-size: 16px;
margin-top: 10px;
width: 100% !important;
height: auto !important;
border-radius: 50px !important;
}
.controls button .cc-btn-text {
display: inline-block !important;
}
.cc-btn-random {
background-color: #f39c12 !important;
color: #ffffff !important;
border: none !important;
cursor: pointer;
}
.cc-btn-random:hover {
background-color: #e67e22 !important;
}
canvas {
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
width: 100%;
height: auto;
display: block;
background-color: #ffffff;
border-radius: 12px;
image-rendering: high-quality;
transform: translateZ(0); /* Hardware acceleration for smoother display */
order: 1;
}
.font-preload { visibility: hidden; position: absolute; height: 0; width: 0; }
@media (min-width: 850px) {
.controls { order: 1; }
canvas { order: 2; }
}
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Main Name:
Subtitle (Optional):
Font Style:
Bubble Letters
Mega Bubble
Soft Block
Heavy Block
Chunky Round
Playful Block
Wavy Retro
Retro Serif
Tall Marker
Doodle Shadow
Bouncy
Comic Book
Varsity / Sports
Western Style
Spooky
Background Pattern:
No Background
Sunburst
Concentric Rings
Spider Web
Wavy Lines
Stained Glass Mesh
Hexagons
Polka Dots
Diamonds
Tile Grid
Triangles
Brick Wall
Stars
Bubbles
Hearts
Crescent Moons
Leaves
Confetti
Flowers
Butterflies
Lightning Bolts
Clouds & Sun
Snowflakes
Music Notes
Page Layout:
Portrait
Landscape
Print / Save Page
Share this Tool
Surprise Me!
(() => {
let canvas, ctx, nameInput, subtextInput, fontSelect, bgSelect, orientationSelect, printBtn, shareToolBtn, randomizeBtn;
function drawCanvas() {
if (!canvas || !ctx || !orientationSelect || !nameInput || !fontSelect || !bgSelect) return;
const isLandscape = orientationSelect.value === ‘landscape’;
canvas.width = isLandscape ? 3300 : 2550;
canvas.height = isLandscape ? 2550 : 3300;
ctx.fillStyle = ‘#FFFFFF’;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = ‘high’;
ctx.strokeStyle = ‘#000000′;
ctx.lineWidth = 24;
ctx.lineJoin = ’round’;
ctx.strokeRect(60, 60, canvas.width – 120, canvas.height – 220);
const bgStyle = bgSelect.value;
const currentFont = fontSelect.value;
const currentName = nameInput.value || ‘ ‘;
const currentSubtext = subtextInput.value.trim();
let mainFontSize = isLandscape ? 500 : 400;
let subFontSize = isLandscape ? 200 : 160;
const maxWidth = canvas.width – 400;
const renderText = () => {
/* Scale Main Name */
ctx.font = `${mainFontSize}px “${currentFont}”`;
let mainTextWidth = ctx.measureText(currentName).width;
if (mainTextWidth > maxWidth) {
mainFontSize = Math.floor(mainFontSize * (maxWidth / mainTextWidth));
ctx.font = `${mainFontSize}px “${currentFont}”`;
mainTextWidth = ctx.measureText(currentName).width;
}
/* Scale Subtitle */
let subTextWidth = 0;
if (currentSubtext) {
ctx.font = `${subFontSize}px “${currentFont}”`;
subTextWidth = ctx.measureText(currentSubtext).width;
if (subTextWidth > maxWidth) {
subFontSize = Math.floor(subFontSize * (maxWidth / subTextWidth));
ctx.font = `${subFontSize}px “${currentFont}”`;
subTextWidth = ctx.measureText(currentSubtext).width;
}
}
const gap = currentSubtext ? 100 : 0;
const totalHeight = mainFontSize + (currentSubtext ? subFontSize : 0) + gap;
const centerY = (canvas.height / 2) – 50;
const startY = centerY – (totalHeight / 2);
const mainY = startY + (mainFontSize / 2);
const subY = startY + mainFontSize + gap + (subFontSize / 2);
const buffer = 100;
const combinedWidth = Math.max(mainTextWidth, subTextWidth);
const textRect = {
left: (canvas.width / 2) – (combinedWidth / 2) – buffer,
right: (canvas.width / 2) + (combinedWidth / 2) + buffer,
top: mainY – (mainFontSize / 2) – buffer,
bottom: currentSubtext ? subY + (subFontSize / 2) + buffer : mainY + (mainFontSize / 2) + buffer
};
ctx.strokeStyle = ‘#000000′;
ctx.lineWidth = 14;
ctx.lineJoin = ’round’;
ctx.lineCap = ’round’;
/* Background Rendering Logic */
if (bgStyle === ‘stars’) drawStars(textRect);
if (bgStyle === ‘bubbles’) drawBubbles(textRect);
if (bgStyle === ‘hearts’) drawHearts(textRect);
if (bgStyle === ‘moons’) drawMoons(textRect);
if (bgStyle === ‘leaves’) drawLeaves(textRect);
if (bgStyle === ‘confetti’) drawConfetti(textRect);
if (bgStyle === ‘flowers’) drawFlowers(textRect);
if (bgStyle === ‘butterflies’) drawButterflies(textRect);
if (bgStyle === ‘lightning’) drawLightning(textRect);
if (bgStyle === ‘clouds’) drawClouds(textRect);
if (bgStyle === ‘snowflakes’) drawSnowflakes(textRect);
if (bgStyle === ‘musicnotes’) drawMusicNotes(textRect);
if (bgStyle === ‘sunburst’) drawSunburst();
if (bgStyle === ‘rings’) drawRings();
if (bgStyle === ‘spiderweb’) drawSpiderWeb();
if (bgStyle === ‘waves’) drawWaves();
if (bgStyle === ‘stainedglass’) drawStainedGlassMesh();
if (bgStyle === ‘hexagons’) drawHexagons(textRect);
if (bgStyle === ‘polkadots’) drawPolkaDots(textRect);
if (bgStyle === ‘diamonds’) drawDiamonds(textRect);
if (bgStyle === ’tiles’) drawTiles(textRect);
if (bgStyle === ‘triangles’) drawTriangles(textRect);
if (bgStyle === ‘bricks’) drawBricks(textRect);
/* Text Rendering Logic */
ctx.textAlign = ‘center’;
ctx.textBaseline = ‘middle’;
ctx.font = `${mainFontSize}px “${currentFont}”`;
ctx.strokeStyle = ‘#FFFFFF’;
ctx.lineWidth = 60;
ctx.strokeText(currentName, canvas.width / 2, mainY);
ctx.strokeStyle = ‘#000000’;
ctx.lineWidth = 28;
ctx.strokeText(currentName, canvas.width / 2, mainY);
ctx.fillStyle = ‘#FFFFFF’;
ctx.fillText(currentName, canvas.width / 2, mainY);
if (currentSubtext) {
ctx.font = `${subFontSize}px “${currentFont}”`;
ctx.strokeStyle = ‘#FFFFFF’;
ctx.lineWidth = 30;
ctx.strokeText(currentSubtext, canvas.width / 2, subY);
ctx.strokeStyle = ‘#000000’;
ctx.lineWidth = 16;
ctx.strokeText(currentSubtext, canvas.width / 2, subY);
ctx.fillStyle = ‘#FFFFFF’;
ctx.fillText(currentSubtext, canvas.width / 2, subY);
}
ctx.font = ‘100px “Fredoka”, sans-serif’;
ctx.fillStyle = ‘#333333’;
ctx.textAlign = ‘center’;
ctx.fillText(‘ColorCove – thousands of free coloring pages’, canvas.width / 2, canvas.height – 80);
};
if (document.fonts && document.fonts.load) {
document.fonts.load(`${mainFontSize}px “${currentFont}”`).catch(e => console.warn(“Font error:”, e)).then(renderText);
} else {
renderText();
}
}
/* — Grid Positions (Fixed missing right-side tiles) — */
function getGridPositions(spacing, padding, textRect) {
const positions = [];
const borderSafeZoneTop = 90;
const borderSafeZoneBottom = 190;
let row = 0;
/* Expand the loops slightly past the edges. The clip() will cut them flush. */
let startY = borderSafeZoneTop – spacing;
let endY = canvas.height – borderSafeZoneBottom + spacing;
let startX = borderSafeZoneTop – spacing;
let endX = canvas.width – borderSafeZoneTop + spacing;
for (let y = startY; y < endY; y += spacing) {
let offsetX = (row % 2 === 0) ? 0 : spacing / 2;
for (let x = startX + offsetX; x textRect.left && x – padding textRect.top && y – padding < textRect.bottom) {
isOverlappingText = true;
}
}
if (!isOverlappingText) positions.push({ x, y });
}
row++;
}
return positions;
}
function getSafePositions(targetCount, minSize, maxSize, padding, textRect) {
const positions = [];
const maxAttempts = 600;
const borderSafeZoneTop = 90;
const borderSafeZoneBottom = 190;
for (let i = 0; i < targetCount; i++) {
let attempts = 0;
let foundSafeSpot = false;
while (!foundSafeSpot && attempts {
const distance = Math.hypot(pos.x – x, pos.y – y);
return distance < (pos.size + size + padding);
});
let isOverlappingText = false;
if (textRect) {
let testX = x, testY = y;
if (x textRect.right) testX = textRect.right;
if (y textRect.bottom) testY = textRect.bottom;
let distX = x – testX, distY = y – testY;
if (Math.sqrt((distX*distX) + (distY*distY)) <= (size + padding)) {
isOverlappingText = true;
}
}
if (!isOverlappingShapes && !isOverlappingText) {
positions.push({ x, y, size });
foundSafeSpot = true;
}
attempts++;
}
}
return positions;
}
/* — Full Page Dividers — */
function drawStainedGlassMesh() {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const cols = 9;
const rows = 12;
const cellW = canvas.width / cols;
const cellH = canvas.height / rows;
const points = [];
for (let y = 0; y <= rows; y++) {
for (let x = 0; x <= cols; x++) {
let px = x * cellW + (Math.random() – 0.5) * cellW * 0.4;
let py = y * cellH + (Math.random() – 0.5) * cellH * 0.4;
points.push({x: px, y: py});
}
}
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
let p1 = points[y * (cols + 1) + x];
let p2 = points[y * (cols + 1) + x + 1];
let p3 = points[(y + 1) * (cols + 1) + x + 1];
let p4 = points[(y + 1) * (cols + 1) + x];
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.lineTo(p4.x, p4.y);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p2.x, p2.y);
ctx.lineTo(p3.x, p3.y);
ctx.lineTo(p4.x, p4.y);
ctx.closePath();
ctx.stroke();
}
}
ctx.restore();
}
function drawWaves() {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
for(let y = 150; y < canvas.height; y += 140) {
ctx.beginPath();
ctx.moveTo(0, y);
for(let x = 0; x <= canvas.width; x += 40) {
ctx.lineTo(x, y + Math.sin(x * 0.015) * 60);
}
ctx.stroke();
}
ctx.restore();
}
function drawRings() {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const cx = canvas.width / 2, cy = (canvas.height / 2) – 50;
const maxRadius = Math.hypot(cx, cy);
for (let r = 100; r < maxRadius; r += 120) {
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.stroke();
}
ctx.restore();
}
function drawSpiderWeb() {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const cx = canvas.width / 2, cy = (canvas.height / 2) – 50;
const maxRadius = Math.hypot(cx, cy);
for (let angle = 0; angle < 360; angle += 30) {
ctx.beginPath();
ctx.moveTo(cx, cy);
let rad = angle * Math.PI / 180;
ctx.lineTo(cx + Math.cos(rad) * maxRadius, cy + Math.sin(rad) * maxRadius);
ctx.stroke();
}
for (let r = 150; r < maxRadius; r += 160) {
ctx.beginPath();
for (let angle = 0; angle <= 360; angle += 30) {
let rad = angle * Math.PI / 180;
let x = cx + Math.cos(rad) * r;
let y = cy + Math.sin(rad) * r;
if (angle === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
ctx.restore();
}
function drawSunburst() {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const cx = canvas.width / 2, cy = (canvas.height / 2) – 50;
const radius = Math.max(canvas.width, canvas.height);
for (let angle = 0; angle < 360; angle += 20) {
ctx.beginPath();
ctx.moveTo(cx, cy);
let rad = angle * Math.PI / 180;
ctx.lineTo(cx + Math.cos(rad) * radius, cy + Math.sin(rad) * radius);
ctx.stroke();
}
ctx.restore();
}
/* — Grid Patterns — */
function drawHexagons(textRect) {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const size = 120;
const hexWidth = size * Math.sqrt(3);
const hexHeight = size * 2;
const xOffset = hexWidth;
const yOffset = hexHeight * 0.75;
const borderSafeZoneTop = 90;
const borderSafeZoneBottom = 190;
/* Expand the bounding loops out so hexagon edges touch the border perfectly */
for (let y = borderSafeZoneTop – yOffset * 2, row = 0; y < canvas.height – borderSafeZoneBottom + hexHeight * 2; y += yOffset, row++) {
let xStart = borderSafeZoneTop – hexWidth * 2 + (row % 2 === 0 ? 0 : hexWidth / 2);
for (let x = xStart; x textRect.left && x – size textRect.top && y – size < textRect.bottom) {
isOverlappingText = true;
}
}
if (!isOverlappingText) {
ctx.beginPath();
for (let i = 0; i {
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
ctx.stroke();
});
ctx.restore();
}
function drawDiamonds(textRect) {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const size = 90;
const spacing = 220;
const positions = getGridPositions(spacing, size, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.beginPath();
ctx.moveTo(0, -size);
ctx.lineTo(size, 0);
ctx.lineTo(0, size);
ctx.lineTo(-size, 0);
ctx.closePath();
ctx.stroke();
ctx.restore();
});
ctx.restore();
}
function drawTiles(textRect) {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const size = 180;
const spacing = 220;
const positions = getGridPositions(spacing, size/2, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.beginPath();
ctx.rect(-size/2, -size/2, size, size);
ctx.rect(-size/3, -size/3, size * 0.66, size * 0.66);
ctx.stroke();
ctx.restore();
});
ctx.restore();
}
function drawTriangles(textRect) {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const size = 160;
const h = size * Math.sqrt(3) / 2;
const borderSafeZoneTop = 90;
const borderSafeZoneBottom = 190;
const drawWidth = canvas.width – (borderSafeZoneTop * 2);
const drawHeight = canvas.height – borderSafeZoneTop – borderSafeZoneBottom;
const offsetX = (drawWidth % size) / 2;
const offsetY = (drawHeight % h) / 2;
for (let y = borderSafeZoneTop + offsetY – h, row = 0; y < canvas.height – borderSafeZoneBottom + h; y += h, row++) {
let xStart = borderSafeZoneTop + offsetX – size + (row % 2 === 0 ? 0 : size / 2);
for (let x = xStart; x textRect.left && x – size*0.5 textRect.top && y – h < textRect.bottom) {
isOverlappingText = true;
}
}
if (!isOverlappingText) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + size/2, y + h);
ctx.lineTo(x – size/2, y + h);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + size, y);
ctx.lineTo(x + size/2, y + h);
ctx.closePath();
ctx.stroke();
}
}
}
ctx.restore();
}
function drawBricks(textRect) {
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
const bWidth = 240;
const bHeight = 100;
const borderSafeZoneTop = 90;
const borderSafeZoneBottom = 190;
const drawWidth = canvas.width – (borderSafeZoneTop * 2);
const drawHeight = canvas.height – borderSafeZoneTop – borderSafeZoneBottom;
const offsetX = (drawWidth % bWidth) / 2;
const offsetY = (drawHeight % bHeight) / 2;
for (let y = borderSafeZoneTop + offsetY – bHeight, row = 0; y < canvas.height – borderSafeZoneBottom + bHeight; y += bHeight, row++) {
let xStart = borderSafeZoneTop + offsetX – bWidth + (row % 2 === 0 ? 0 : bWidth / 2);
for (let x = xStart; x textRect.left && x textRect.top && y {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI * 2);
const style = Math.floor(Math.random() * 3);
if (style === 0) {
/* 6-petal standard */
for(let i=0; i<6; i++) {
ctx.save(); ctx.rotate(i * Math.PI / 3); ctx.beginPath();
ctx.ellipse(pos.size * 0.45, 0, pos.size * 0.45, pos.size * 0.25, 0, 0, Math.PI * 2);
ctx.stroke(); ctx.restore();
}
} else if (style === 1) {
/* 8-petal daisy */
for(let i=0; i<8; i++) {
ctx.save(); ctx.rotate(i * Math.PI / 4); ctx.beginPath();
ctx.ellipse(pos.size * 0.5, 0, pos.size * 0.5, pos.size * 0.15, 0, 0, Math.PI * 2);
ctx.stroke(); ctx.restore();
}
} else {
/* Pointy lotus style */
for(let i=0; i {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI * 2);
let s = pos.size / 2;
const style = Math.random();
if (style > 0.5) {
/* Rounded wings */
ctx.beginPath();
ctx.moveTo(0,0); ctx.bezierCurveTo(s, -s, s*2, -s/2, s, s/2); ctx.lineTo(0,0);
ctx.moveTo(0,0); ctx.bezierCurveTo(-s, -s, -s*2, -s/2, -s, s/2); ctx.lineTo(0,0); ctx.stroke();
ctx.beginPath();
ctx.moveTo(0,0); ctx.bezierCurveTo(s, s, s*1.5, s*1.5, s/2, s); ctx.lineTo(0,0);
ctx.moveTo(0,0); ctx.bezierCurveTo(-s, s, -s*1.5, s*1.5, -s/2, s); ctx.lineTo(0,0); ctx.stroke();
} else {
/* Swallowtail / angular wings */
ctx.beginPath();
ctx.moveTo(0,0); ctx.lineTo(s*1.2, -s*0.8); ctx.lineTo(s*1.5, 0); ctx.lineTo(s*0.8, s*0.4); ctx.lineTo(0,0);
ctx.moveTo(0,0); ctx.lineTo(-s*1.2, -s*0.8); ctx.lineTo(-s*1.5, 0); ctx.lineTo(-s*0.8, s*0.4); ctx.lineTo(0,0); ctx.stroke();
ctx.beginPath();
ctx.moveTo(0,0); ctx.lineTo(s, s*0.6); ctx.lineTo(s*0.8, s*1.2); ctx.lineTo(s*0.3, s*0.9); ctx.lineTo(0,0);
ctx.moveTo(0,0); ctx.lineTo(-s, s*0.6); ctx.lineTo(-s*0.8, s*1.2); ctx.lineTo(-s*0.3, s*0.9); ctx.lineTo(0,0); ctx.stroke();
}
/* Antennas */
ctx.beginPath();
ctx.moveTo(0, -s*0.6); ctx.quadraticCurveTo(s*0.3, -s*1.2, s*0.5, -s*1.1);
ctx.moveTo(0, -s*0.6); ctx.quadraticCurveTo(-s*0.3, -s*1.2, -s*0.5, -s*1.1);
ctx.stroke();
/* Body */
ctx.beginPath();
ctx.ellipse(0, 0, s*0.2, s*0.8, 0, 0, Math.PI*2);
ctx.fillStyle = ‘#FFFFFF’;
ctx.fill();
ctx.stroke();
/* Body segments */
ctx.beginPath();
ctx.moveTo(-s*0.18, 0); ctx.lineTo(s*0.18, 0);
ctx.moveTo(-s*0.15, s*0.3); ctx.lineTo(s*0.15, s*0.3);
ctx.moveTo(-s*0.15, -s*0.3); ctx.lineTo(s*0.15, -s*0.3);
ctx.stroke();
ctx.restore();
});
}
/* NEW: Lightning Bolts */
function drawLightning(textRect) {
const positions = getSafePositions(12, 200, 400, 30, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI * 2);
let s = pos.size; /* Visual size perfectly matches collision radius */
ctx.beginPath();
ctx.moveTo(s*0.2, -s);
ctx.lineTo(-s*0.5, s*0.1);
ctx.lineTo(s*0.1, s*0.1);
ctx.lineTo(-s*0.3, s);
ctx.lineTo(s*0.5, -s*0.1);
ctx.lineTo(-s*0.1, -s*0.1);
ctx.closePath();
ctx.stroke();
if (Math.random() > 0.5) {
/* Secondary smaller bolt, precisely shifted to avoid overlapping the first */
ctx.beginPath();
ctx.moveTo(s*0.72, -s*0.5);
ctx.lineTo(s*0.30, s*0.16);
ctx.lineTo(s*0.66, s*0.16);
ctx.lineTo(s*0.42, s*0.70);
ctx.lineTo(s*0.90, s*0.04);
ctx.lineTo(s*0.54, s*0.04);
ctx.closePath();
ctx.stroke();
}
ctx.restore();
});
}
function drawMoons(textRect) {
const positions = getSafePositions(35, 100, 250, 40, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI * 2);
if (Math.random() > 0.4) {
/* Draw Crescent Moon */
ctx.beginPath();
ctx.arc(0, 0, pos.size, Math.PI * 1.5, Math.PI * 0.5, false);
ctx.arc(-pos.size * 0.4, 0, pos.size * 0.9, Math.PI * 0.5, Math.PI * 1.5, true);
ctx.closePath();
ctx.stroke();
} else {
/* Draw Independent Star */
ctx.beginPath();
let sr = pos.size * 0.8; // Much bigger star
for (let j = 0; j {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI * 2);
const style = Math.random();
ctx.beginPath();
if (style > 0.5) {
/* Classic smooth leaf */
ctx.moveTo(0, -pos.size);
ctx.quadraticCurveTo(pos.size, 0, 0, pos.size);
ctx.quadraticCurveTo(-pos.size, 0, 0, -pos.size);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, -pos.size + 10); ctx.lineTo(0, pos.size – 10);
ctx.stroke();
} else {
/* Jagged wide leaf */
ctx.moveTo(0, -pos.size);
ctx.bezierCurveTo(pos.size * 0.8, -pos.size * 0.5, pos.size * 0.6, pos.size * 0.8, 0, pos.size);
ctx.bezierCurveTo(-pos.size * 0.6, pos.size * 0.8, -pos.size * 0.8, -pos.size * 0.5, 0, -pos.size);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, -pos.size + 10); ctx.lineTo(0, pos.size – 10);
ctx.moveTo(0, -pos.size * 0.3); ctx.lineTo(pos.size * 0.3, -pos.size * 0.5);
ctx.moveTo(0, -pos.size * 0.3); ctx.lineTo(-pos.size * 0.3, -pos.size * 0.5);
ctx.moveTo(0, pos.size * 0.2); ctx.lineTo(pos.size * 0.4, 0);
ctx.moveTo(0, pos.size * 0.2); ctx.lineTo(-pos.size * 0.4, 0);
ctx.stroke();
}
ctx.restore();
});
}
function drawStars(textRect) {
const positions = getSafePositions(20, 140, 280, 40, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
const style = Math.random();
ctx.beginPath();
if (style > 0.4) {
/* Classic 5-point star */
ctx.rotate(Math.random() * Math.PI);
for (let j = 0; j < 5; j++) {
ctx.lineTo(Math.cos((18 + j * 72) * Math.PI / 180) * pos.size, -Math.sin((18 + j * 72) * Math.PI / 180) * pos.size);
ctx.lineTo(Math.cos((54 + j * 72) * Math.PI / 180) * (pos.size / 2.5), -Math.sin((54 + j * 72) * Math.PI / 180) * (pos.size / 2.5));
}
ctx.closePath();
} else {
/* 4-point magical sparkle */
for (let j = 0; j {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.beginPath();
ctx.arc(0, 0, pos.size, 0, Math.PI * 2);
ctx.stroke();
/* Bubble reflection curve */
ctx.beginPath(); ctx.arc(0, 0, pos.size * 0.7, Math.PI * 1.2, Math.PI * 1.7); ctx.stroke();
/* Tiny bubble dot */
ctx.beginPath(); ctx.arc(pos.size * 0.5, -pos.size * 0.5, pos.size * 0.1, 0, Math.PI * 2); ctx.stroke();
ctx.restore();
});
}
function drawHearts(textRect) {
const positions = getSafePositions(25, 120, 240, 30, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate((Math.random() – 0.5) * 0.5);
const style = Math.random();
ctx.beginPath();
ctx.moveTo(0, pos.size * 0.8);
ctx.bezierCurveTo(-pos.size, 0, -pos.size, -pos.size * 0.8, 0, -pos.size * 0.4);
ctx.bezierCurveTo(pos.size, -pos.size * 0.8, pos.size, 0, 0, pos.size * 0.8);
ctx.closePath();
ctx.stroke();
if (style > 0.6) {
/* Inner heart */
const inner = pos.size * 0.5;
ctx.beginPath();
ctx.moveTo(0, inner * 0.8); ctx.bezierCurveTo(-inner, 0, -inner, -inner * 0.8, 0, -inner * 0.4); ctx.bezierCurveTo(inner, -inner * 0.8, inner, 0, 0, inner * 0.8);
ctx.closePath(); ctx.stroke();
} else if (style > 0.3) {
/* Outside sparkle lines */
ctx.beginPath();
ctx.moveTo(-pos.size * 0.8, -pos.size * 0.6); ctx.lineTo(-pos.size * 1.1, -pos.size * 0.9);
ctx.moveTo(pos.size * 0.8, -pos.size * 0.6); ctx.lineTo(pos.size * 1.1, -pos.size * 0.9);
ctx.moveTo(0, -pos.size * 0.6); ctx.lineTo(0, -pos.size * 1.0); ctx.stroke();
}
ctx.restore();
});
}
function drawConfetti(textRect) {
const positions = getSafePositions(35, 100, 220, 20, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI);
const style = Math.random();
ctx.beginPath();
if (style > 0.6) {
ctx.rect(-pos.size/2, -pos.size/2, pos.size, pos.size);
} else if (style > 0.3) {
ctx.moveTo(0, -pos.size/2);
ctx.lineTo(pos.size/2, pos.size/2);
ctx.lineTo(-pos.size/2, pos.size/2);
ctx.closePath();
} else {
/* Squiggly ribbons */
ctx.moveTo(-pos.size/2, -pos.size/2);
ctx.bezierCurveTo(0, -pos.size, pos.size, 0, 0, pos.size/2);
ctx.bezierCurveTo(-pos.size, pos.size, 0, pos.size*1.5, pos.size/2, pos.size);
}
ctx.stroke();
ctx.restore();
});
}
/* NEW: Snowflakes */
function drawSnowflakes(textRect) {
const hasSubtext = !!document.getElementById(‘subtextInput’).value.trim();
const count = hasSubtext ? 20 : 35;
const positions = getSafePositions(count, 120, 250, 30, textRect);
positions.forEach(pos => {
ctx.save();
ctx.lineWidth = 8; /* Thinner lines so details don’t turn into a blob */
ctx.translate(pos.x, pos.y);
ctx.rotate(Math.random() * Math.PI);
const style = Math.random();
const branches = style > 0.5 ? 4 : 3;
const angleStep = Math.PI / branches;
for (let i = 0; i 0.7) {
/* Geometric centers & circle tips – this is already colorable */
ctx.beginPath();
ctx.moveTo(pos.size * 0.3, 0); ctx.lineTo(0, pos.size * 0.3); ctx.lineTo(-pos.size * 0.3, 0); ctx.lineTo(0, -pos.size * 0.3); ctx.closePath();
ctx.stroke();
ctx.beginPath(); ctx.arc(pos.size, 0, pos.size * 0.15, 0, Math.PI*2); ctx.stroke();
ctx.beginPath(); ctx.arc(-pos.size, 0, pos.size * 0.15, 0, Math.PI*2); ctx.stroke();
} else {
/* Make V shapes into closed triangles */
ctx.beginPath();
ctx.moveTo(pos.size * 0.5, pos.size * 0.3); ctx.lineTo(pos.size * 0.8, 0); ctx.lineTo(pos.size * 0.5, -pos.size * 0.3); ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-pos.size * 0.5, pos.size * 0.3); ctx.lineTo(-pos.size * 0.8, 0); ctx.lineTo(-pos.size * 0.5, -pos.size * 0.3); ctx.closePath();
ctx.stroke();
}
ctx.rotate(angleStep);
}
ctx.restore();
});
}
/* NEW: Music Notes */
function drawMusicNotes(textRect) {
const hasSubtext = !!document.getElementById(‘subtextInput’).value.trim();
const count = hasSubtext ? 25 : 40;
const positions = getSafePositions(count, 120, 280, 30, textRect);
positions.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate((Math.random() – 0.5) * 0.5);
const s = pos.size; /* Visual size now correctly matches collision radius */
if (Math.random() > 0.5) {
ctx.beginPath();
ctx.ellipse(-s*0.3, s*0.6, s*0.4, s*0.3, -0.2, 0, Math.PI * 2);
ctx.moveTo(s*0.1, s*0.6);
ctx.lineTo(s*0.1, -s*0.8);
ctx.bezierCurveTo(s*0.1, -s*0.8, s*0.6, -s*0.6, s*0.8, -s*0.2);
} else {
ctx.beginPath();
ctx.ellipse(-s*0.6, s*0.6, s*0.3, s*0.2, -0.2, 0, Math.PI * 2);
ctx.ellipse(s*0.6, s*0.4, s*0.3, s*0.2, -0.2, 0, Math.PI * 2);
ctx.moveTo(-s*0.3, s*0.6); ctx.lineTo(-s*0.3, -s*0.6);
ctx.lineTo(s*0.9, -s*0.8); ctx.lineTo(s*0.9, s*0.4);
ctx.moveTo(-s*0.3, -s*0.3); ctx.lineTo(s*0.9, -s*0.5);
}
ctx.fillStyle = ‘#FFFFFF’;
ctx.fill();
ctx.stroke();
ctx.restore();
});
}
function drawClouds(textRect) {
const hasSubtext = !!document.getElementById(‘subtextInput’).value.trim();
ctx.save();
ctx.beginPath();
ctx.rect(72, 72, canvas.width – 144, canvas.height – 244);
ctx.clip();
ctx.save();
ctx.translate(400, 400);
ctx.beginPath();
ctx.arc(0, 0, 180, 0, Math.PI * 2);
ctx.stroke();
for(let a = 0; a {
const dist = Math.hypot(pos.x – 400, pos.y – 400);
return dist > (380 + pos.size);
});
safeClouds.forEach(pos => {
ctx.save();
ctx.translate(pos.x, pos.y);
let s = pos.size; /* Visual size now matches the collision radius perfectly */
ctx.beginPath();
ctx.moveTo(-s*0.6, s*0.3);
ctx.bezierCurveTo(-s, s*0.3, -s, -s*0.2, -s*0.5, -s*0.3);
ctx.bezierCurveTo(-s*0.4, -s*0.8, s*0.2, -s*0.9, s*0.4, -s*0.4);
ctx.bezierCurveTo(s*0.9, -s*0.4, s*0.9, s*0.3, s*0.5, s*0.3);
ctx.closePath();
ctx.fillStyle = ‘#FFFFFF’;
ctx.fill();
ctx.stroke();
ctx.restore();
});
ctx.restore();
}
function initColoringGenerator() {
canvas = document.getElementById(‘coloringCanvas’);
if (!canvas || !document.getElementById(‘nameInput’) || !document.getElementById(‘bgSelect’)) return false; /* Prevent script from crashing if DOM isn’t fully ready */
ctx = canvas.getContext(‘2d’);
nameInput = document.getElementById(‘nameInput’);
subtextInput = document.getElementById(‘subtextInput’);
fontSelect = document.getElementById(‘fontSelect’);
bgSelect = document.getElementById(‘bgSelect’);
orientationSelect = document.getElementById(‘orientationSelect’);
printBtn = document.getElementById(‘printBtn’);
shareToolBtn = document.getElementById(‘shareToolBtn’);
randomizeBtn = document.getElementById(‘randomizeBtn’);
/* Pick a random background (excluding ‘none’) on initial load */
const bgOptions = Array.from(bgSelect.options).map(o => o.value).filter(v => v !== ‘none’);
bgSelect.value = bgOptions[Math.floor(Math.random() * bgOptions.length)];
nameInput.addEventListener(‘input’, drawCanvas);
subtextInput.addEventListener(‘input’, drawCanvas);
fontSelect.addEventListener(‘change’, drawCanvas);
bgSelect.addEventListener(‘change’, drawCanvas);
orientationSelect.addEventListener(‘change’, drawCanvas);
if (randomizeBtn) {
randomizeBtn.addEventListener(‘click’, () => {
const fonts = Array.from(fontSelect.options).map(o => o.value);
fontSelect.value = fonts[Math.floor(Math.random() * fonts.length)];
const bgs = Array.from(bgSelect.options).map(o => o.value).filter(v => v !== ‘none’);
bgSelect.value = bgs[Math.floor(Math.random() * bgs.length)];
drawCanvas();
});
}
printBtn.addEventListener(‘click’, () => {
const originalContent = printBtn.innerHTML;
printBtn.innerHTML = ‘
Preparing… ‘;
printBtn.style.pointerEvents = ‘none’;
printBtn.style.opacity = ‘0.7’;
/* Track the generation event with all settings */
if (typeof window.gtag === ‘function’) {
window.gtag(‘event’, ‘generate_custom_name’, {
‘custom_name’: (nameInput.value || ‘ColorCove’).trim(),
‘custom_subtitle’: subtextInput.value.trim(),
‘custom_font’: fontSelect.value,
‘custom_background’: bgSelect.value,
‘custom_orientation’: orientationSelect.value
});
}
setTimeout(() => {
try {
const dataUrl = canvas.toDataURL(‘image/png’);
/* Create a clean file name using the first 8 letters of the user’s custom name */
const safeName = (nameInput.value || ”).substring(0, 8).replace(/[^a-z0-9]/gi, ”).toLowerCase();
const fileName = `colorcove-${safeName || ‘custom-name’}.png`;
/* Hook into the website’s native print modal which has robust, pre-configured CSS */
if (typeof window.ccOpenPrintModal === ‘function’) {
window.ccOpenPrintModal(dataUrl, fileName);
/* — FIX: Defeat aggressive WordPress/PWA Caching by replacing the buttons AT RUNTIME — */
/* Use a brief timeout to ensure the modal DOM is fully initialized before grabbing buttons */
setTimeout(() => {
[‘modal-print-btn’, ‘modal-download-btn’, ‘modal-share-btn’].forEach(id => {
let btn = document.getElementById(id);
if (btn && !btn.dataset.ccIntercepted) {
let newBtn = btn.cloneNode(true);
newBtn.dataset.ccIntercepted = ‘true’;
btn.parentNode.replaceChild(newBtn, btn);
newBtn.addEventListener(‘click’, async () => {
const targetImg = document.getElementById(‘modal-target-img’);
if (!targetImg) return;
const isData = targetImg.src.startsWith(‘data:’);
const fName = targetImg.getAttribute(‘data-filename’) || (isData ? ‘colorcove-custom.png’ : (targetImg.src.split(‘/’).pop().split(‘?’)[0] || ‘image.png’));
let eventAction = ‘share_custom_name’;
if (id === ‘modal-download-btn’) eventAction = ‘download_custom_name’;
if (id === ‘modal-print-btn’) eventAction = ‘print_custom_name’;
if (typeof window.gtag === ‘function’) {
window.gtag(‘event’, eventAction, {
‘file_name’: fName,
‘custom_name’: (nameInput.value || ‘ColorCove’).trim(),
‘custom_subtitle’: subtextInput.value.trim(),
‘custom_font’: fontSelect.value,
‘custom_background’: bgSelect.value,
‘custom_orientation’: orientationSelect.value
});
}
const originalBtnContent = newBtn.innerHTML;
let actionText = ‘Processing…’;
if (id === ‘modal-download-btn’) actionText = ‘Saving…’;
else if (id === ‘modal-share-btn’) actionText = ‘Sharing…’;
else if (id === ‘modal-print-btn’) actionText = ‘Preparing…’;
newBtn.innerHTML = `
${actionText} `;
newBtn.style.pointerEvents = ‘none’;
newBtn.style.opacity = ‘0.7’;
/* Delay execution to allow the browser to paint the loading icon */
setTimeout(async () => {
if (id === ‘modal-print-btn’) {
window.print();
setTimeout(() => {
newBtn.innerHTML = originalBtnContent;
newBtn.style.pointerEvents = ‘auto’;
newBtn.style.opacity = ‘1’;
}, 2000);
return;
} else if (id === ‘modal-download-btn’) {
try {
const response = await fetch(targetImg.src);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const a = document.createElement(‘a’);
a.style.display = ‘none’;
a.href = blobUrl;
a.download = fName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => window.URL.revokeObjectURL(blobUrl), 100);
} catch (err) {
const a = document.createElement(‘a’);
a.href = targetImg.src;
a.download = fName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
} else if (id === ‘modal-share-btn’) {
if (navigator.share) {
try {
const response = await fetch(targetImg.src);
const blob = await response.blob();
const file = new File([blob], fName, { type: blob.type });
const shareData = {
title: document.title,
text: ‘Check out this awesome coloring page!’,
url: window.location.href
};
/* Feature detect if the specific device allows passing files through Web Share */
if (navigator.canShare && navigator.canShare({ files: [file] })) {
shareData.files = [file];
}
await navigator.share(shareData);
} catch (err) {
console.warn(“Share failed:”, err);
}
} else {
navigator.clipboard.writeText(window.location.href);
alert(‘Page link copied to clipboard!’);
}
}
/* Leave the loading icon on screen briefly so the user sees it */
setTimeout(() => {
newBtn.innerHTML = originalBtnContent;
newBtn.style.pointerEvents = ‘auto’;
newBtn.style.opacity = ‘1’;
}, 1500);
}, 50);
});
}
});
}, 100);
} else {
/* Fallback if the native modal isn’t present */
const printWindow = window.open(”, ‘_blank’);
if (printWindow) {
printWindow.document.title = ‘Print’;
printWindow.document.body.style.cssText = ‘margin:0;display:flex;justify-content:center;align-items:center;height:100vh;’;
const img = printWindow.document.createElement(‘img’);
img.style.cssText = ‘max-width:100%;max-height:100%;object-fit:contain;’;
img.onload = () => { printWindow.print(); printWindow.close(); };
img.src = dataUrl;
printWindow.document.body.appendChild(img);
printWindow.document.close();
} else {
alert(“Your browser blocked the print popup! Please allow popups or use the download option instead.”);
}
}
} catch(e) {
console.warn(“Print failed:”, e);
alert(“Could not generate print image. Please try downloading instead.”);
} finally {
printBtn.innerHTML = originalContent;
printBtn.style.pointerEvents = ‘auto’;
printBtn.style.opacity = ‘1’;
}
}, 50);
});
if (shareToolBtn) {
shareToolBtn.addEventListener(‘click’, async () => {
if (navigator.share) {
try {
await navigator.share({
title: document.title,
text: ‘Create your own custom name coloring pages for free!’,
url: window.location.href
});
} catch (err) {
console.warn(‘Share failed:’, err);
}
} else {
navigator.clipboard.writeText(window.location.href);
alert(‘Tool link copied to clipboard!’);
}
});
}
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(() => { drawCanvas(); });
}
setTimeout(drawCanvas, 100);
setTimeout(drawCanvas, 500);
return true; /* Successfully initialized */
}
/* Robust poller to defeat WordPress caching/delay plugins */
function waitForGenerator() {
if (!initColoringGenerator()) {
setTimeout(waitForGenerator, 100);
}
}
waitForGenerator();
})();
(function() {
var template = document.getElementById(“cc-app-code”);
if (!template) return;
// 1. Get the raw text (WordPress mangles symbols like && into < > &)
var rawCode = template.innerHTML;
// 2. Decode HTML entities by passing them through a textarea
var txt = document.createElement(“textarea”);
txt.innerHTML = rawCode;
var decodedCode = txt.value;
// 3. Strip rogue WordPress paragraph
or tags injected into the text
decodedCode = decodedCode.replace(//g, ‘\n’).replace(/ /gi, ‘\n’);
// 4. Inject and execute the clean JavaScript securely
var scriptObj = document.createElement(“script”);
scriptObj.textContent = decodedCode;
document.body.appendChild(scriptObj);
})();
How to Make Your Personalized Coloring Page
Creating your custom coloring sheet takes less than 10 seconds:
Enter Your Main Text: Type a name, a vocabulary word, or a short phrase into the first box.
Add a Subtitle (Optional): Perfect for adding dates, “Happy Birthday,” or “First Day of School!”
Choose Your Font Style: Pick from our library of extra-thick, colorable fonts like Mega Bubble , Playful Block , or Tall Marker .
Select a Background Pattern: Surround your name with fun scatters like Stars and Crescent Moons, or try a full-page divider like our geometric Stained Glass Mesh.
Print or Download: Hit “Print” to send it straight to your printer perfectly centered, or “Download” to save a high-resolution image for later.
A Note on Privacy & Safety: We know how important digital privacy is for families and classrooms. Because this generator is powered entirely by your own web browser, the names and custom text you enter are never sent to our servers, saved, or tracked in any way . Your creations are 100% private, secure, and exist only on your screen until you hit print.
Not Just for Names! Creative Ways to Use This Tool
While this is the ultimate name generator, you can type any word you want. Here are a few ways our community uses this tool:
Birthday Party Favors: Print a custom page for every guest attending the party.
Classroom Activities: Teachers love printing personalized desk labels or “Student of the Week” awards.
Spelling & Sight Words: Type in weekly spelling words to make learning and handwriting practice fun for early readers.
Custom Event Banners: Print single letters on individual pages to color and string together for custom party banners.
Holiday Greetings: Create custom coloring cards that say “Happy Halloween” or “Merry Christmas” using our themed fonts and backgrounds.
Why Parents & Teachers Love the ColorCove Generator
100% Free & Instant: Your browser does all the work, meaning your page generates instantly without waiting in a server queue.
Printer-Friendly & Ink-Saving: We built this tool with pure white base layers. You will never accidentally waste expensive printer ink printing hidden gray backgrounds.
High-Resolution (300 DPI): Every page is perfectly calibrated for standard 8.5″ x 11″ paper so your lines stay crisp, bold, and easy to color.
Smart Collision Detection: Our advanced layout engine ensures your name is always the star of the show. Background patterns automatically part ways to leave a clean, colorable space around your text.
Why We Built the ColorCove Generator
As someone who manages printable activities, we noticed a huge gap: finding high-quality, truly customizable name coloring pages usually meant paying for digital downloads or dealing with clunky, ad-heavy websites. I wanted to build a lightning-fast, ink-saving tool that parents and teachers could use for free, without having to sign up or trade their email addresses. I hope your family and classroom enjoy it as much as we do!
Frequently Asked Questions (FAQ)
Can I print my coloring page in landscape mode?
Yes! Just use the orientation dropdown menu in the tool to switch between Portrait and Landscape before you hit print. The generator will automatically resize your text to fit the new layout.
What kind of paper should I use?
For crayons and colored pencils, standard copy paper works great. If you plan on using markers or watercolors, we recommend printing your custom page on heavy cardstock to prevent bleeding.
Is it really free? Can I print as many as I want?
Absolutely. You can generate and print as many personalized coloring pages as you need for your home, classroom, or event.