Please bookmark this page to avoid losing your image tool!

Image Neolithic Petroglyph Frame Creator

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, frameWidthPercent = 0.15, frameBaseColor = '#B08D57', glyphColor = '#5D4037', glyphDensity = 0.4, edgeRoughness = 8) {

    // Helper: Hex to RGB
    function hexToRgb(hex) {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : { r: 0, g: 0, b: 0 }; // Default to black if parsing fails
    }

    // Helper: RGB to Hex
    function componentToHex(c) {
        const ch = Math.max(0, Math.min(255, Math.round(c))); // Clamp and round
        const hex = ch.toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }

    function rgbToHex(r, g, b) {
        return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
    }

    // Helper: Random float between -range/2 and range/2
    const R_offset = (range) => (Math.random() - 0.5) * range;

    // 1. Setup canvas and calculate dimensions
    const actualFrameWidth = Math.max(10, Math.max(originalImg.width, originalImg.height) * frameWidthPercent); // Min frame width 10px
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width + 2 * actualFrameWidth;
    canvas.height = originalImg.height + 2 * actualFrameWidth;
    const ctx = canvas.getContext('2d');

    // 2. Fill frame background color
    ctx.fillStyle = frameBaseColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 3. Add speckle texture
    const baseRgb = hexToRgb(frameBaseColor);
    const numSpeckles = Math.floor(canvas.width * canvas.height / 30); // Density of speckles
    for (let i = 0; i < numSpeckles; i++) {
        const x = Math.random() * canvas.width;
        const y = Math.random() * canvas.height;
        const offset = (Math.random() - 0.5) * 60; // RGB variation amount
        const speckleR = baseRgb.r + offset;
        const speckleG = baseRgb.g + offset;
        const speckleB = baseRgb.b + offset;
        ctx.fillStyle = rgbToHex(speckleR, speckleG, speckleB);
        ctx.fillRect(x, y, Math.random() * 2 + 1, Math.random() * 2 + 1); // Speckle size
    }

    // 4. Draw original image with rough edges (if edgeRoughness > 0)
    ctx.save();
    const imgX = actualFrameWidth;
    const imgY = actualFrameWidth;
    const imgW = originalImg.width;
    const imgH = originalImg.height;

    if (edgeRoughness > 0 && actualFrameWidth > 0) {
        const ER = Math.min(edgeRoughness, actualFrameWidth * 0.5); // Cap roughness
        ctx.beginPath();
        const pointsPerEdge = 5; // Number of intermediate points on each edge for roughness
        
        let currentX = imgX + R_offset(ER);
        let currentY = imgY + R_offset(ER);
        ctx.moveTo(currentX, currentY);

        // Top edge
        for (let i = 1; i < pointsPerEdge; i++) {
            ctx.lineTo(imgX + (imgW * i / pointsPerEdge) + R_offset(ER), imgY + R_offset(ER));
        }
        ctx.lineTo(imgX + imgW + R_offset(ER), imgY + R_offset(ER));
        
        // Right edge
        for (let i = 1; i < pointsPerEdge; i++) {
            ctx.lineTo(imgX + imgW + R_offset(ER), imgY + (imgH * i / pointsPerEdge) + R_offset(ER));
        }
        ctx.lineTo(imgX + imgW + R_offset(ER), imgY + imgH + R_offset(ER));

        // Bottom edge
        for (let i = pointsPerEdge - 1; i >= 1; i--) {
             ctx.lineTo(imgX + (imgW * i / pointsPerEdge) + R_offset(ER), imgY + imgH + R_offset(ER));
        }
        ctx.lineTo(imgX + R_offset(ER), imgY + imgH + R_offset(ER));
        
        // Left edge
        for (let i = pointsPerEdge - 1; i >= 1; i--) {
            ctx.lineTo(imgX + R_offset(ER), imgY + (imgH * i / pointsPerEdge) + R_offset(ER));
        }
        ctx.closePath();
        ctx.clip();
    }
    ctx.drawImage(originalImg, imgX, imgY, imgW, imgH);
    ctx.restore();

    // 5. Draw Petroglyphs
    if (actualFrameWidth >= 5) { // Only draw glyphs if frame is somewhat thick
        const numGlyphs = Math.floor(glyphDensity * 150); // Max 150 glyphs at density 1
        const baseGlyphLineWidth = Math.max(1, Math.floor(actualFrameWidth * 0.05));
        const minGlyphSize = actualFrameWidth * 0.15;
        const maxGlyphSize = actualFrameWidth * 0.7;

        ctx.strokeStyle = glyphColor;
        ctx.fillStyle = glyphColor; // For dots or filled shapes
        ctx.lineCap = 'round';
        ctx.lineJoin = 'round';

        const glyphTypes = [
            'line', 'arc', 'dots', 'spiral', 'wavy', 'stickfigure', 'sun'
        ];

        for (let i = 0; i < numGlyphs; i++) {
            const glyphSize = minGlyphSize + Math.random() * (maxGlyphSize - minGlyphSize);
            const glyphLineWidth = Math.max(1, baseGlyphLineWidth + R_offset(baseGlyphLineWidth * 0.5));
            ctx.lineWidth = glyphLineWidth;

            let gx, gy;
            const randBand = Math.random();

            // Determine band and position (gx, gy are center of glyph)
            if (randBand < 0.25) { // Top band
                gx = glyphSize / 2 + Math.random() * (canvas.width - glyphSize);
                gy = glyphSize / 2 + Math.random() * (actualFrameWidth - glyphSize);
            } else if (randBand < 0.5) { // Bottom band
                gx = glyphSize / 2 + Math.random() * (canvas.width - glyphSize);
                gy = canvas.height - actualFrameWidth + glyphSize / 2 + Math.random() * (actualFrameWidth - glyphSize);
            } else if (randBand < 0.75) { // Left band
                gx = glyphSize / 2 + Math.random() * (actualFrameWidth - glyphSize);
                gy = actualFrameWidth + glyphSize / 2 + Math.random() * (originalImg.height - glyphSize);
            } else { // Right band
                gx = canvas.width - actualFrameWidth + glyphSize / 2 + Math.random() * (actualFrameWidth - glyphSize);
                gy = actualFrameWidth + glyphSize / 2 + Math.random() * (originalImg.height - glyphSize);
            }
            
            if (gy + glyphSize/2 > canvas.height || gy - glyphSize/2 < 0 || 
                gx + glyphSize/2 > canvas.width || gx - glyphSize/2 < 0 ||
                (gy - glyphSize/2 < actualFrameWidth && gy + glyphSize/2 > actualFrameWidth) || // Avoid if frame is too thin by chance
                (gy - glyphSize/2 < canvas.height - actualFrameWidth && gy + glyphSize/2 > canvas.height - actualFrameWidth) ||
                (gx - glyphSize/2 < actualFrameWidth && gx + glyphSize/2 > actualFrameWidth) ||
                (gx - glyphSize/2 < canvas.width - actualFrameWidth && gx + glyphSize/2 > canvas.width - actualFrameWidth)
            ) {
                 if (actualFrameWidth < glyphSize) continue; // Skip if glyph is too big for the band
            }


            const glyphType = glyphTypes[Math.floor(Math.random() * glyphTypes.length)];
            ctx.beginPath();

            switch (glyphType) {
                case 'line': {
                    const angle = Math.random() * 2 * Math.PI;
                    const length = glyphSize * (0.5 + Math.random() * 0.5);
                    ctx.moveTo(gx - Math.cos(angle) * length / 2, gy - Math.sin(angle) * length / 2);
                    ctx.lineTo(gx + Math.cos(angle) * length / 2, gy + Math.sin(angle) * length / 2);
                    break;
                }
                case 'arc': {
                    const radius = glyphSize / 2 * (0.7 + Math.random() * 0.3);
                    const startAngle = Math.random() * 2 * Math.PI;
                    const endAngle = startAngle + Math.PI * (0.5 + Math.random()); // Arc length
                    ctx.arc(gx, gy, radius, startAngle, endAngle);
                    break;
                }
                case 'dots': {
                    const numDots = 3 + Math.floor(Math.random() * 4);
                    for (let k = 0; k < numDots; k++) {
                        const dotX = gx + R_offset(glyphSize * 0.8);
                        const dotY = gy + R_offset(glyphSize * 0.8);
                        const dotRadius = glyphLineWidth * (0.5 + Math.random() * 0.5);
                        ctx.moveTo(dotX + dotRadius, dotY); // moveTo for arc fill
                        ctx.arc(dotX, dotY, dotRadius, 0, 2 * Math.PI);
                    }
                    ctx.fill(); // Use fill for dots
                    continue; // skip stroke for this type
                }
                case 'spiral': {
                    const maxRadius = glyphSize / 2;
                    const rotations = 1.5 + Math.random() * 2;
                    ctx.moveTo(gx, gy);
                    for (let k = 0; k <= 50; k++) {
                        const progress = k / 50;
                        const angle = progress * rotations * 2 * Math.PI;
                        const r = progress * maxRadius * (0.8 + Math.random() * 0.2);
                        const sx = gx + r * Math.cos(angle);
                        const sy = gy + r * Math.sin(angle);
                        ctx.lineTo(sx, sy);
                    }
                    break;
                }
                case 'wavy': {
                    const waves = 3 + Math.floor(Math.random() * 3);
                    const amplitude = glyphSize * 0.15;
                    const length = glyphSize;
                    const startX = gx - length / 2;
                    const startY = gy;
                    ctx.moveTo(startX, startY);
                    for (let k = 0; k <= waves * 2; k++) {
                        const t = k / (waves * 2);
                        const wx = startX + t * length;
                        const wy = startY + Math.sin(t * waves * Math.PI) * amplitude * (0.7 + Math.random()*0.6) + R_offset(amplitude*0.3);
                        ctx.lineTo(wx, wy);
                    }
                    break;
                }
                case 'stickfigure': { // Very abstract stick figure
                    const bodyH = glyphSize * 0.6;
                    const headR = glyphSize * 0.15;
                    const limbL = glyphSize * 0.3;

                    // Head
                    ctx.moveTo(gx + headR, gy - bodyH / 2 - headR);
                    ctx.arc(gx, gy - bodyH / 2 - headR, headR, 0, 2 * Math.PI);
                    // Body
                    ctx.moveTo(gx, gy - bodyH / 2);
                    ctx.lineTo(gx, gy + bodyH / 2);
                    // Arms
                    ctx.moveTo(gx - limbL, gy - bodyH * 0.1);
                    ctx.lineTo(gx, gy - bodyH * 0.25);
                    ctx.lineTo(gx + limbL, gy - bodyH * 0.1);
                    // Legs
                    ctx.moveTo(gx - limbL, gy + bodyH / 2 + limbL * 0.6);
                    ctx.lineTo(gx, gy + bodyH / 2);
                    ctx.lineTo(gx + limbL, gy + bodyH / 2 + limbL * 0.6);
                    break;
                }
                case 'sun': {
                    const coreRadius = glyphSize * 0.2;
                    const rayLength = glyphSize * 0.25;
                    const numRays = 6 + Math.floor(Math.random() * 5);
                    ctx.moveTo(gx + coreRadius, gy);
                    ctx.arc(gx, gy, coreRadius, 0, 2 * Math.PI);
                    for(let k=0; k < numRays; k++) {
                        const angle = (k / numRays) * 2 * Math.PI;
                        ctx.moveTo(gx + Math.cos(angle) * (coreRadius+glyphLineWidth/2), gy + Math.sin(angle) * (coreRadius+glyphLineWidth/2));
                        ctx.lineTo(gx + Math.cos(angle) * (coreRadius + rayLength), gy + Math.sin(angle) * (coreRadius + rayLength));
                    }
                    break;
                }
            }
            ctx.stroke();
        }
    }

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Neolithic Petroglyph Frame Creator is an online tool designed to enhance images by adding a decorative petroglyph-style frame. Users can specify the width and color of the frame, as well as customize the appearance of petroglyph glyphs that are randomly generated around the edges. This tool is ideal for artists, designers, and hobbyists looking to create unique and visually appealing borders for their images, suitable for use in digital art, presentations, or as part of marketing materials.

Leave a Reply

Your email address will not be published. Required fields are marked *

Other Image Tools:

Image Ukiyo-e Japanese Woodblock Print Creator

Image Persian Miniature Painting Creator

Image Sci-Fi Movie Poster Template Creator

Image Horror Movie Poster Template

Image Social Media Milestone Certificate Creator

Halloween Death Certificate Template

Image Anatomical Illustration Frame Creator

Image Romance Novel Cover Template Creator

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

Image Birth Certificate Template Generator

Image Driver’s License Template Creator

Image Scout Explorer Badge Template Creator

Image Steampunk Document Frame Creator

Image Vintage Scientific Illustration Frame Creator

See All →