Advanced script to extract the dominant color and generate dynamic gradients

Advanced script to extract the dominant color and generate dynamic gradients

Advanced script to extract the dominant color and generate dynamic gradients

Dominant Color Extraction with Gradient

This JavaScript script analyzes an image to extract its dominant colors and applies a dynamic gradient to a container. The text color is automatically set based on the average contrast to ensure readability.

Instructions for Use

  1. Assign the ID “myImage” to the image widget and “targetDiv” to the container.
  2. Paste the JavaScript script before the closing </body> tag or inside an HTML/JS widget.
  3. The script will analyze the image, calculate the dominant colors, and apply the gradient to the target container.
  4. It works best if the image is same-origin or if the server allows Access-Control-Allow-Origin.

Optional parameters in the script: sampling canvas size, color quantization level, brightness filters, and cluster size.

Result: the target container displays a gradient between the dominant colors of the image, with automatically readable text.

				
					
/*
  Estrattore colore dominante veloce e robusto (no librerie esterne).
  - Cambia imageContainerId e targetContainerId se necessario.
  - Funziona meglio se l'immagine è same-origin o il server fornisce Access-Control-Allow-Origin.
*/
(async function() {
  const imageContainerId = 'myImage';    // elemento che contiene <img>
  const targetContainerId = 'targetDiv'; // container Elementor da colorare

  // Parametri (regolabili)
  const SAMPLE_SIZE = 200;    // lato massiccio del canvas di sampling (200x200 mantiene qualità/speed)
  const QUANTIZE_LEVEL = 24;  // quantizzazione colore (più basso = più bin)
  const MIN_PIXEL_COUNT = 20; // ignora cluster troppo piccoli
  const IGNORE_BRIGHTNESS_ABOVE = 0.94; // ignora near-white
  const IGNORE_BRIGHTNESS_BELOW = 0.03; // ignora near-black

  function rgbToHex(r,g,b) {
    return '#' + [r,g,b].map(v =&gt; v.toString(16).padStart(2,'0')).join('');
  }

  function rgbToHsl(r,g,b) {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    let h=0, s=0, l=(max+min)/2;
    if (max !== min) {
      const d = max-min;
      s = l&gt;0.5 ? d/(2-max-min) : d/(max+min);
      switch(max) {
        case r: h = (g-b)/d + (g<b> rgb (minima) ---
  function hslToRgb(h, s, l) {
    let r, g, b;
    if (s === 0) {
      r = g = b = l; // achromatic
    } else {
      const hue2rgb = (p, q, t) =&gt; {
        if (t  1) t -= 1;
        if (t &lt; 1/6) return p + (q - p) * 6 * t;
        if (t &lt; 1/2) return q;
        if (t &lt; 2/3) return p + (q - p) * (2/3 - t) * 6;
        return p;
      };
      const q = l  v/255).map(v =&gt; v  {
      // se l'elemento esistente è same-origin e già caricato, lo usiamo
      try {
        const urlObj = new URL(url, location.href);
        if (urlObj.origin === location.origin &amp;&amp; img.complete &amp;&amp; img.naturalWidth &gt; 0) {
          return resolve(img);
        }
      } catch(e) { /* ignore */ }

      const i = new Image();
      i.crossOrigin = 'anonymous'; // deve essere impostato PRIMA di src
      const t = setTimeout(() =&gt; reject(new Error('Timeout image load')), timeout);
      i.onload = () =&gt; { clearTimeout(t); resolve(i); };
      i.onerror = (e) =&gt; { clearTimeout(t); reject(new Error('Errore caricamento immagine')); };
      i.src = url;
    });
  }

  // quantizzazione semplice per raggruppare colori
  function quantizeKey(r,g,b,level) {
    // mappa 0..255 su 0..level-1
    const q = v =&gt; Math.floor(v * (level-1) / 255);
    return `${q(r)}_${q(g)}_${q(b)}`;
  }

  // main sampling + clustering
  async function computeDominant(imgEl) {
    // canvas dimension keeping aspect ratio
    const w = imgEl.naturalWidth || imgEl.width;
    const h = imgEl.naturalHeight || imgEl.height;
    if (!w || !h) throw new Error('Immagine senza dimensioni');

    // dimensione canvas di sampling: manteniamo lato massimo SAMPLE_SIZE
    const scale = Math.min(1, SAMPLE_SIZE / Math.max(w,h));
    const cw = Math.max(1, Math.round(w * scale));
    const ch = Math.max(1, Math.round(h * scale));

    const canvas = document.createElement('canvas');
    canvas.width = cw;
    canvas.height = ch;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(imgEl, 0, 0, cw, ch);

    const data = ctx.getImageData(0,0,cw,ch).data;
    const map = new Map();
    let totalCount = 0;

    // optionally exclude big white margins by focusing sampling area in center
    const cx0 = Math.floor(cw * 0.08), cy0 = Math.floor(ch * 0.08);
    const cx1 = Math.ceil(cw * 0.92), cy1 = Math.ceil(ch * 0.92);

    for (let y = cy0; y &lt; cy1; y++) {
      for (let x = cx0; x &lt; cx1; x++) {
        const idx = (y * cw + x) * 4;
        const r = data[idx], g = data[idx+1], b = data[idx+2], a = data[idx+3];
        if (a  ignora
        // filtro luminosità per scartare sfondi quasi bianchi/neri
        const [hue, sat, lum] = rgbToHsl(r,g,b);
        if (lum &gt; IGNORE_BRIGHTNESS_ABOVE || lum &lt; IGNORE_BRIGHTNESS_BELOW) continue;
        // quantizza
        const key = quantizeKey(r,g,b,QUANTIZE_LEVEL);
        let entry = map.get(key);
        if (!entry) {
          entry = {count:0, rSum:0, gSum:0, bSum:0};
          map.set(key, entry);
        }
        entry.count++;
        entry.rSum += r;
        entry.gSum += g;
        entry.bSum += b;
        totalCount++;
      }
    }

    if (totalCount === 0) throw new Error(&#039;Nessun pixel valido (forse immagine sfondo bianco o CORS)&#039;);

    // crea array di cluster medi
    const clusters = [];
    for (const [k,e] of map.entries()) {
      if (e.count  {
      const lumFactor = 1 - Math.min(0.5, Math.abs(c.l - 0.5)); // 0..0.5 -&gt; 0.5..1
      c.score = c.count * (0.5 + c.s) * (0.5 + lumFactor);
    });

    clusters.sort((a,b) =&gt; b.score - a.score);

    // top cluster
    const top = clusters[0];
    return {
      hex: rgbToHex(top.r, top.g, top.b),
      rgb: [top.r, top.g, top.b],
      count: top.count,
      score: top.score,
      clusters // utile per debug / scelta dei top 2
    };
  }

  // esegui flusso
  let loaded;
  try {
    loaded = await loadImage(src);
  } catch (e) {
    // fallback: se immagine originale è caricata e same-origin, usala
    if (img.complete &amp;&amp; img.naturalWidth &gt; 0) {
      loaded = img;
    } else {
      console.error('Impossibile caricare immagine per analisi:', e);
      return;
    }
  }

  let result;
  try {
    result = await computeDominant(loaded);
  } catch (e) {
    console.error('Errore computazione dominante:', e);
    return;
  }

  // --- Modifica: prendiamo due colori e applichiamo gradiente ---
  const top1 = result.clusters &amp;&amp; result.clusters[0];
  const top2 = result.clusters &amp;&amp; result.clusters[1];

  let hex1 = result.hex;
  let rgb1 = result.rgb;

  let hex2, rgb2;
  if (top2) {
    rgb2 = [top2.r, top2.g, top2.b];
    hex2 = rgbToHex(...rgb2);
  } else {
    // se non c'è un secondo cluster, generiamo un colore "complementare leggermente ruotato" del primo
    const [h, s, l] = rgbToHsl(...rgb1);
    const newH = (h + 0.08) % 1; // ruota la tinta di ~29° (0.08*360)
    rgb2 = hslToRgb(newH, s, Math.min(0.9, Math.max(0.1, l))); // mantieni s/l in range
    hex2 = rgbToHex(...rgb2);
  }

  // applica gradiente da alto-sinistra a basso-destra (to bottom right)
  target.style.backgroundImage = `linear-gradient(to bottom right, ${hex1}, ${hex2})`;
  // rimuoviamo backgroundColor per evitare conflitti
  target.style.backgroundColor = 'transparent';

  // scegli colore testo in base al contrast ratio approssimativo (lum media)
  const lumAvg = (getLuminance(...rgb1) + getLuminance(...rgb2)) / 2;
  target.style.color = (lumAvg &gt; 0.55) ? '#000' : '#fff';
  // opzionale: se il tuo contenitore ha elementi con colori fissi (pulsanti) potresti voler cambiare solo una overlay

  console.log('Dominant colors chosen:', hex1, hex2, 'details:', {result, rgb2});

})();


				
			

Privacy Policy

Informativa sul trattamento dei dati personali ai sensi dell’Art. 13 del Regolamento (UE) 2016/679 (GDPR)

Data Ultimo Aggiornamento: 06/11/25


1. Identità e dati di contatto del Titolare del Trattamento

Titolare del trattamento dei dati personali è:

Angelo Marra
Indirizzo: Via Marcantonio Franceschini 10, 40128 Bologna (BO)
Partita IVA: IT04068171208
Codice Fiscale: MRRNGL80B13A509N
Email: angelo@marrisonlab.com
PEC: angelomarra80@pec.it


2. Dati trattati, Finalità e Base Giuridica

2.1. Dati Raccolti e Finalità (Modulo Contatti)

Dati Raccolti: Nome, Cognome, Indirizzo email, Messaggio e qualsiasi altro dato spontaneamente inserito dall’utente nel modulo.

Finalità: I Suoi Dati Personali sono trattati esclusivamente per rispondere alle Sue richieste di informazione, contatto, preventivo e/o per adempiere a misure precontrattuali adottate su Sua richiesta.

Base Giuridica: La base giuridica del trattamento è l’esecuzione di misure precontrattuali adottate su richiesta dello stesso (Art. 6.1, lett. b, GDPR).

2.2. Dati di Navigazione (Esplicativo)

I sistemi informatici acquisiscono dati (es. indirizzi IP) la cui trasmissione è implicita nell’uso dei protocolli di Internet. Tali dati sono utilizzati al solo fine di ricavare informazioni statistiche anonime e controllarne il corretto funzionamento. Si rimanda alla Cookie Policy per i dettagli sull’uso dei cookie.


3. Periodo di Conservazione dei Dati e Destinatari

Modalità del Trattamento: Il trattamento dei dati avviene mediante strumenti manuali, informatici e telematici.

Conservazione: I dati raccolti tramite il modulo di contatto sono conservati per il tempo strettamente necessario a evadere la Sua richiesta, salvo diversi obblighi di legge.

Destinatari: I Suoi dati non saranno diffusi. Potranno essere trattati da soggetti autorizzati (collaboratori) e da soggetti esterni (es. fornitori di servizi hosting) che agiscono come Responsabili del Trattamento, opportunamente nominati.


4. I Suoi Diritti (Art. 15-22 GDPR)

In qualità di Interessato, Lei ha il diritto di esercitare i seguenti diritti in qualsiasi momento:

  • Diritto di accesso (Art. 15)
  • Diritto di rettifica (Art. 16)
  • Diritto alla cancellazione (Diritto all’Oblio, Art. 17)
  • Diritto di limitazione di trattamento (Art. 18)
  • Diritto di opposizione al trattamento (Art. 21)
  • Diritto alla portabilità dei dati (Art. 20)

Per esercitare tali diritti, La preghiamo di contattare il Titolare all’indirizzo email angelo@marrisonlab.com o PEC angelomarra80@pec.it.

Inoltre, Lei ha il diritto di proporre reclamo all’Autorità di Controllo competente (Garante per la Protezione dei Dati Personali – www.garanteprivacy.it).

1
Asistente IA
Online · Guest
Hi! I'm Marrison Assistant, your virtual assistant. How can I help you?
Now
By writing in this chat you accept our privacy policy.
Powered By Marrisonlab