May 30th

Animation Configurator — Customize our animation library with ease! Animation Configurator!

Notify me

Documentation

v1.9

Premium element

Core Background

Aura Flow

Element provides a dynamic, colorful fluid animation effect within a container. This immersive effect mimics fluid motions and responds to user interactions like mouse movement, creating an engaging visual experience.

Quick Setup

data-fluid

Required

Activates the Aura Flow effect.

Available Presets

data-fluid-preset="preset-name"

Optional

ocean

sunset

pink

warm

rainbow

golden

mystic

tropical

neon

Custom Colors

Define your own color scheme using these attributes

data-fluid-color-1

Optional

Sets the first color in the fluid animation

Example: #FF0000, rgb(255,0,0), red

data-fluid-color-2

Optional

Example: #00FF00, rgb(0,255,0), green

Example: #00FF00, rgb(0,255,0), green

data-fluid-color-3

Optional

Example: #0000FF, rgb(0,0,255), blue

Example: #0000FF, rgb(0,0,255), blue

Advanced configuration

data-fluid-opacity

Optional

Sets the opacity level of the fluid animation

Default: 0.5

data-fluid-intensity

Optional

Controls the overall color intensity of the effect

Default: 0.8

data-fluid-glow

Optional

Adjusts the glow effect seen in the animation

Default: 0.8

data-fluid-radius

Optional

Specifies the radius of the effect reach

Default: 100

data-fluid-pulse-speed

Optional

Sets the speed at which the animation pulses

Default: 0.5

We're here to help

Choose how you'd like to connect with our support team

Contact Support

Get help from our team of experts

Response within 24 hours

<!DOCTYPE html>
<html lang="en">
<head>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Aura Flow Configurator</title>
  <style>
    :root {
  --background: #121212;
  --card-bg: #1e1e1e;
  --card-bg-hover: #252525;
  --text-primary: #f2f2f7;
  --text-secondary: #8e8e93;
  --accent: #ef6013;
  --accent-hover: #c64c0c;
  --border: #2c2c2e;
  --shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  --track: #2c2c2e;
  --thumb: #ef6013;
  --card-radius: 16px;
  --input-radius: 8px;
  --button-radius: 12px;
  --transition: all 0.25s ease;
  --font: 'Inter', BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
  
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font);
  background-color: var(--background);
  color: var(--text-primary);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.container {
  max-width: 100%;
  margin: 0 auto;
  padding: 2.5rem 1.5rem;
}

.content {
  display: flex;
  flex-wrap: wrap;
  gap: 2.5rem;
}

.preview-section {
  flex: 1;
  min-width: 300px;
}

.controls-section {
  flex: 1;
  min-width: 300px;
  max-width: 500px;
}

.card {
  background-color: var(--card-bg);
  border-radius: var(--card-radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 2rem;
  border: 1px solid var(--border);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}

.preview-container {
  height: 500px;
  width: 100%;
  position: relative;
  overflow: hidden;
  border-radius: var(--card-radius);
  background-color: #000000;
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
}

.preview-content {
  color: white;
  text-align: center;
  font-weight: bold;
  font-size: var(--text-s);
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.card-heading {
  padding: 1.25rem 1.5rem;
  font-size: var(--text-s);
  font-weight: 600;
  border-bottom: 1px solid var(--border);
  letter-spacing: 0.3px;
}

.card-content {
  padding: 1.75rem;
}

.control-group {
  margin-bottom: 1.75rem;
}

.control-group:last-child {
  margin-bottom: 0;
}

.control-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
}

.label-text {
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.2px;
}

.value-text {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  background-color: rgba(50, 50, 50, 0.5);
  padding: 2px 8px;
  border-radius: 4px;
}

input[type="range"] {
  -webkit-appearance: none;
  width: 100%;
  height: 4px;
  background: var(--track);
  border-radius: 2px;
  outline: none;
  margin: 0.8rem 0;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  background: var(--thumb);
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

input[type="range"]::-webkit-slider-thumb:hover {
  transform: scale(1.1);
  box-shadow: 0 0 8px rgba(239, 96, 19, 0.5);
}

input[type="range"]::-moz-range-thumb {
  width: 18px;
  height: 18px;
  background: var(--thumb);
  border: none;
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

input[type="range"]::-moz-range-thumb:hover {
  transform: scale(1.1);
  box-shadow: 0 0 8px rgba(239, 96, 19, 0.5);
}

input[type="text"],
input[type="number"],
select {
  width: 100%;
  padding: 0.8rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--input-radius);
  font-family: var(--font);
  font-size: var(--text-xs);
  color: var(--text-primary);
  background-color: rgba(30, 30, 30, 0.7);
  margin-bottom: 0.75rem;
  outline: none;
  transition: var(--transition);
}

input[type="text"]:focus,
input[type="number"]:focus,
select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}

.color-mode {
  display: flex;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.radio-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  position: relative;
  padding-left: 1.5rem;
  font-size: var(--text-xs);
}

.radio-label input {
  position: absolute;
  opacity: 0;
  cursor: pointer;
}

.radio-checkmark {
  position: absolute;
  top: 2px;
  left: 0;
  height: 16px;
  width: 16px;
  background-color: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 50%;
}

.radio-label input:checked ~ .radio-checkmark {
  background-color: var(--card-bg);
  border-color: var(--accent);
}

.radio-checkmark:after {
  content: "";
  position: absolute;
  display: none;
}

.radio-label input:checked ~ .radio-checkmark:after {
  display: block;
  top: 3px;
  left: 3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

.color-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.8rem;
  margin-bottom: 1.2rem;
}

.color-input-group {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--input-radius);
  padding: 0.4rem;
  background-color: rgba(30, 30, 30, 0.7);
  transition: var(--transition);
}

.color-input-group:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}

.color-preview {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  width: 24px;
  height: 24px;
  border-radius: 4px;
  pointer-events: none;
}

input[type="color"] {
  -webkit-appearance: none;
  border: none;
  width: 100%;
  height: 24px;
  cursor: pointer;
  background-color: transparent;
}

input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}

input[type="color"]::-webkit-color-swatch {
  border: none;
  border-radius: 4px;
}

input[type="color"]::-moz-color-swatch {
  border: none;
  border-radius: 4px;
}

.color-text {
  padding-left: 2.5rem;
  border: none;
  background-color: transparent;
  color: var(--text-secondary);
  font-size: 0.8rem;
  letter-spacing: 0.3px;
}

.color-text:focus {
  color: var(--text-primary);
  box-shadow: none;
}

.switch-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.2rem;
  padding: 0.5rem 0;
}

.switch-label {
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.2px;
}

.switch {
  position: relative;
  display: inline-block;
  width: 52px;
  height: 28px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: var(--track);
  transition: var(--transition);
  border-radius: 34px;
}

.slider:before {
  position: absolute;
  content: "";
  height: 20px;
  width: 20px;
  left: 4px;
  bottom: 4px;
  background-color: #f2f2f7;
  transition: var(--transition);
  border-radius: 50%;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}

input:checked + .slider {
  background-color: var(--accent);
}

input:focus + .slider {
  box-shadow: 0 0 1px var(--accent);
}

input:checked + .slider:before {
  transform: translateX(24px);
}

.btn {
  display: block;
  width: 100%;
  padding: 1rem 1.5rem;
  background-color: var(--accent);
  color: white;
  font-family: var(--font);
  font-size: var(--text-xs);
  font-weight: 600;
  text-align: center;
  border: none;
  border-radius: var(--button-radius);
  cursor: pointer;
  transition: var(--transition);
  letter-spacing: 0.3px;
}

.btn:hover {
  background-color: var(--accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(239, 96, 19, 0.3);
}

.btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(239, 96, 19, 0.2);
}

.instructions {
  background-color: var(--card-bg);
  padding: 1.75rem;
  border-radius: var(--card-radius);
  margin-bottom: 2rem;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
}

.instructions h3 {
  font-size: var(--text-s);
  margin-bottom: 1rem;
  color: var(--text-primary);
  font-weight: 600;
  letter-spacing: 0.3px;
}

.instructions ol {
  padding-left: 1.5rem;
}

.instructions li {
  margin-bottom: 0.75rem;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.6;
}

.instructions code {
  background-color: rgba(50, 50, 50, 0.5);
  padding: 0.2rem 0.5rem;
  border-radius: 4px;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: var(--text-xs);
  color: #ff8c51;
}

.instructions strong {
  font-weight: 500;
  color: var(--text-primary);
}

.notice {
  background-color: rgba(239, 96, 19, 0.08);
  color: #ff8c51;
  padding: 1rem 1.2rem;
  border-radius: var(--input-radius);
  font-size: var(--text-xs);
  margin-top: 1.5rem;
  border-left: 3px solid var(--accent);
  letter-spacing: 0.2px;
  line-height: 1.6;
}

.code-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  visibility: hidden;
  opacity: 0;
  transition: all 0.3s ease;
  backdrop-filter: blur(5px);
}

.code-modal.active {
  visibility: visible;
  opacity: 1;
}

.modal-content {
  width: 90%;
  max-width: 1000px;
  max-height: 90vh;
  background-color: var(--card-bg);
  border-radius: var(--card-radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  transform: translateY(20px);
  transition: all 0.3s ease;
  border: 1px solid var(--border);
}

.code-modal.active .modal-content {
  transform: translateY(0);
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid var(--border);
}

.modal-title {
  font-size: var(--text-s);
  font-weight: 600;
  letter-spacing: 0.3px;
}

.close-modal {
  background: none;
  border: none;
  font-size: var(--text-s);
  cursor: pointer;
  color: var(--text-secondary);
  transition: var(--transition);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-modal:hover {
  color: var(--text-primary);
  background-color: rgba(255, 255, 255, 0.1);
}

.modal-body {
  padding: 1.75rem;
  overflow-y: auto;
  max-height: calc(90vh - 130px);
}

.code-display {
  background-color: #161616;
  color: #e4e4e4;
  border-radius: var(--card-radius);
  padding: 1.5rem;
  overflow-x: auto;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: var(--text-xs);
  line-height: 1.6;
  white-space: pre;
  margin-bottom: 1rem;
  max-height: 400px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid #252525;
}

.modal-footer {
  padding: 1.25rem 1.75rem;
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: flex-end;
  gap: 1rem;
}

.copy-btn,
.download-file-btn {
  padding: 0.8rem 1.2rem;
  background-color: var(--accent);
  color: white;
  font-family: var(--font);
  font-size: var(--text-xs);
  font-weight: 500;
  border: none;
  border-radius: var(--button-radius);
  cursor: pointer;
  transition: var(--transition);
  letter-spacing: 0.2px;
}

.copy-btn:hover,
.download-file-btn:hover {
  background-color: var(--accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}

.copy-btn:active,
.download-file-btn:active {
  transform: scale(0.98);
  box-shadow: 0 2px 8px rgba(239, 96, 19, 0.2);
}

.premium-card {
  margin-top: 2rem;
  border: 1px solid var(--border);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  background: linear-gradient(145deg, #212121, #1e1e1e);
}

.premium-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  border-color: rgba(239, 96, 19, 0.3);
}

.premium-badge {
  display: inline-block;
  background: linear-gradient(90deg, #ef6013, #ff8c51);
  color: white;
  font-size: var(--text-xs);
  padding: 0.3rem 0.7rem;
  border-radius: 6px;
  margin-right: 0.75rem;
  font-weight: 600;
  vertical-align: middle;
  letter-spacing: 0.3px;
  box-shadow: 0 2px 8px rgba(239, 96, 19, 0.3);
}

.premium-description {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  margin-bottom: 1.5rem;
  line-height: 1.7;
  letter-spacing: 0.2px;
}

.feature-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.75rem 0;
}

.feature-list li {
  position: relative;
  padding-left: 1.75rem;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin-bottom: 0.9rem;
  letter-spacing: 0.2px;
  line-height: 1.5;
}

.feature-list li:before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--accent);
  font-weight: bold;
}

.premium-btn {
  background: linear-gradient(90deg, #ef6013, #ff8c51);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  letter-spacing: 0.3px;
}

.premium-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(239, 96, 19, 0.4);
}

.btn-icon {
  font-size: 1.1em;
}

.copy-attribute {
  display: inline-flex;
  align-items: center;
  background-color: rgba(50, 50, 50, 0.5);
  padding: 0.2rem 0.5rem;
  border-radius: 4px;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: var(--text-xs);
  color: #ff8c51;
  cursor: pointer;
  transition: var(--transition);
  margin: 0 0.2rem;
}

.copy-attribute:hover {
  background-color: rgba(239, 96, 19, 0.2);
}

.copy-icon {
  margin-left: 0.5rem;
  font-size: 0.9em;
  opacity: 0.7;
}

.copy-attribute:hover .copy-icon {
  opacity: 1;
}

.blend-mode-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.8rem;
}

.blend-mode-option {
  font-size: var(--text-xs);
  padding: 0.75rem;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: var(--input-radius);
  cursor: pointer;
  transition: var(--transition);
  background-color: rgba(30, 30, 30, 0.7);
}

.blend-mode-option.active {
  background-color: var(--accent);
  color: white;
  border-color: var(--accent);
  box-shadow: 0 2px 8px rgba(239, 96, 19, 0.4);
}
  </style>
</head>
<body>
  <div class="container">
    <div class="instructions">
      <h3>How to Use Aura Flow in Bricks Builder</h3>
      <ol>
        <li>Design your Aura Flow animation effect using the controls below</li>
        <li>Click the <strong>Get Code</strong> button and copy the JavaScript</li>
        <li>In Bricks Builder, add a <strong>Code</strong> element and paste the JavaScript</li>
        <li>Add the attribute <div class="copy-attribute" data-attribute="data-aura-flow">data-aura-flow<span class="copy-icon">📋</span></div> to any section you want to apply the Aura Flow effect to</li>
      </ol>
      <div class="notice">
        This tool creates a custom script with your settings built-in. You only need to add the data attribute to see your exact configuration!
      </div>
    </div>

    <div class="content">
      <section class="preview-section">
        <div class="preview-container" id="fluid-preview" data-aura-flow="true">
          <div class="preview-content"></div>
        </div>
        
        <div class="card premium-card">
          <div class="card-heading">
            <span class="premium-badge">BricksFusion Exclusive</span>
            Get Your Custom Aura Flow Animation
          </div>
          <div class="card-content">
            <p class="premium-description">Create stunning interactive Aura Flow animations for your Bricks Builder projects with this custom-crafted WebGL animation.</p>
            <ul class="feature-list">
              <li>Easy implementation with a single attribute</li>
              <li>Interactive mouse-follow animations</li>
              <li>Fully customizable colors and effects</li>
              <li>Performance optimized for all devices</li>
            </ul>
            <button class="btn premium-btn" id="download-btn" data-protection-animation="true">
              <span class="btn-icon">⚡</span>
              Get Instant Code
            </button>
          </div>
        </div>
      </section>

      <section class="controls-section">
        <div class="card">
          <div class="card-heading">Colors & Appearance</div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Color Palette</span>
              </div>
              <div class="color-grid">
                <div class="color-input-group">
                  <input type="color" id="color1" value="#4444ff">
                  <input type="text" id="color1-text" class="color-text" value="#4444ff">
                </div>
                <div class="color-input-group">
                  <input type="color" id="color2" value="#0088ff">
                  <input type="text" id="color2-text" class="color-text" value="#0088ff">
                </div>
                <div class="color-input-group">
                  <input type="color" id="color3" value="#00ffff">
                  <input type="text" id="color3-text" class="color-text" value="#00ffff">
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Opacity</span>
                <span class="value-text"><span id="opacity-value">0.9</span></span>
              </div>
              <input type="range" id="opacity" min="0.1" max="1" value="0.9" step="0.05">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">Effect Properties</div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Intensity</span>
                <span class="value-text"><span id="intensity-value">1.0</span></span>
              </div>
              <input type="range" id="intensity" min="0.1" max="2" value="1.0" step="0.1">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Glow Effect</span>
                <span class="value-text"><span id="glow-value">0.8</span></span>
              </div>
              <input type="range" id="glow" min="0.1" max="2" value="0.8" step="0.1">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Electric Effect</span>
                <span class="value-text"><span id="electric-value">1.2</span></span>
              </div>
              <input type="range" id="electric" min="0.1" max="2" value="1.2" step="0.1">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Effect Radius</span>
                <span class="value-text"><span id="radius-value">100</span></span>
              </div>
              <input type="range" id="radius" min="10" max="300" value="100" step="5">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">Animation Settings</div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Animation Speed</span>
                <span class="value-text"><span id="speed-value">1.0</span></span>
              </div>
              <input type="range" id="speed" min="0.1" max="2" value="1.0" step="0.1">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Pulse Speed</span>
                <span class="value-text"><span id="pulse-speed-value">8.0</span></span>
              </div>
              <input type="range" id="pulse-speed" min="1" max="15" value="8.0" step="0.5">
            </div>
            
            <div class="control-group">
                <div class="control-label">
                  <span class="label-text">Mouse Transition</span>
                  <span class="value-text"><span id="transition-value">0.08</span></span>
                </div>
                <input type="range" id="transition" min="0.01" max="0.2" value="0.08" step="0.01">
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  
    <div class="code-modal" id="code-modal">
      <div class="modal-content">
        <div class="modal-header">
          <h3 class="modal-title">JavaScript Code</h3>
          <button class="close-modal" id="close-modal">×</button>
        </div>
        <div class="modal-body">
          <div class="code-display" id="js-code"></div>
        </div>
        <div class="modal-footer">
          <button class="copy-btn" id="copy-btn">Copy to Clipboard</button>
          <button class="download-file-btn" id="download-file-btn">Download JS File</button>
        </div>
      </div>
    </div>
  
    <script>
      document.addEventListener('DOMContentLoaded', function() {
        // Configuration object for the fluid animation
        let fluidConfig = {
          colors: ['#4444ff', '#0088ff', '#00ffff'],
          opacity: 0.9,
          intensity: 1.0,
          glow: 0.8,
          electric: 1.2,
          radius: 100,
          speed: 1.0,
          pulseSpeed: 8.0,
          transition: 0.08
        };
        
        // Active fluid animation instance
        let activeFluid = null;
        
        // Function to convert hex to RGB array (0-1 range for WebGL)
        function hexToRGB(hex) {
          try {
            if (!hex || typeof hex !== 'string') {
              console.error('Invalid hex value provided:', hex);
              return [0.5, 0.5, 1.0]; // Default blue color
            }
            
            // Make sure hex starts with #
            if (hex.charAt(0) !== '#') {
              hex = '#' + hex;
            }
            
            const shorthand = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
            hex = hex.replace(shorthand, (m, r, g, b) => {
              return '#' + r + r + g + g + b + b;
            });
            
            const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
            if (!result) {
              console.error('Could not parse hex value:', hex);
              return [0.5, 0.5, 1.0]; // Default blue color
            }
            
            const r = parseInt(result[1], 16) / 255;
            const g = parseInt(result[2], 16) / 255;
            const b = parseInt(result[3], 16) / 255;
            
            return [r, g, b];
          } catch (error) {
            console.error('Error in hexToRGB:', error, 'for hex value:', hex);
            return [0.5, 0.5, 1.0]; // Default blue color
          }
        }
        
        // Function to initialize or update the fluid animation
        function initFluidAnimation() {
          destroyExistingFluid();
          
          // Get the preview container
          const container = document.getElementById('fluid-preview');
          
          // Set data attributes based on current configuration
          container.setAttribute('data-aura-flow', 'true');
          container.setAttribute('data-aura-flow-color1', fluidConfig.colors[0]);
          container.setAttribute('data-aura-flow-color2', fluidConfig.colors[1]);
          container.setAttribute('data-aura-flow-color3', fluidConfig.colors[2]);
          container.setAttribute('data-aura-flow-opacity', fluidConfig.opacity);
          container.setAttribute('data-aura-flow-intensity', fluidConfig.intensity);
          container.setAttribute('data-aura-flow-glow', fluidConfig.glow);
          container.setAttribute('data-aura-flow-electric', fluidConfig.electric);
          container.setAttribute('data-aura-flow-radius', fluidConfig.radius);
          container.setAttribute('data-aura-flow-speed', fluidConfig.speed);
          container.setAttribute('data-aura-flow-pulse-speed', fluidConfig.pulseSpeed);
          container.setAttribute('data-aura-flow-transition', fluidConfig.transition);
          
          // Initialize the new FluidAnimation
          activeFluid = new FluidAnimation(container);
        }
        
        // Function to destroy existing fluid animation
        function destroyExistingFluid() {
          if (activeFluid) {
            activeFluid.destroy();
            activeFluid = null;
          }
        }
        
        // FluidAnimation class implementation
        class FluidAnimation {
          constructor(container) {
            this.container = container;
            this._cache = {
              dimensions: { width: 0, height: 0, aspect: 0 },
              mouse: { current: { x: 0.5, y: 0.5 }, last: { x: 0.5, y: 0.5 } },
              time: 0,
              frameCount: 0,
              autonomous: true
            };
            
            this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
            this._frameInterval = this.isMobile ? 1000/30 : 1000/60;
            this._lastFrameTime = 0;
            this.mouseX = 0.5;
            this.mouseY = 0.5;
            this.lastX = 0.5;
            this.lastY = 0.5;
            this.targetX = 0.5;
            this.targetY = 0.5;
            this.transitionSpeed = parseFloat(container.getAttribute('data-aura-flow-transition')) || fluidConfig.transition;
            this.isMouseDown = false;
            
            this.config = this._parseConfig(container);
            this._setup(container);
            this._initWebGL();
            this._setupShaders();
            this._setupEvents();
            this._animate();
          }
          
          _parseConfig(container) {
            // Extract colors from data attributes or use defaults
            const colors = [];
            
            for (let i = 1; i <= 3; i++) {
              const colorAttr = 'data-aura-flow-color' + i;
              if (container.hasAttribute(colorAttr)) {
                const hexColor = container.getAttribute(colorAttr);
                colors.push(hexToRGB(hexColor));
              }
            }
            
            // If no colors specified, use default colors
            if (colors.length !== 3) {
              colors.length = 0; // Clear the array
              colors.push(hexToRGB(fluidConfig.colors[0]));
              colors.push(hexToRGB(fluidConfig.colors[1]));
              colors.push(hexToRGB(fluidConfig.colors[2]));
            }
            
            return {
              colors: colors,
              opacity: parseFloat(container.getAttribute('data-aura-flow-opacity')) || fluidConfig.opacity,
              intensity: parseFloat(container.getAttribute('data-aura-flow-intensity')) || fluidConfig.intensity,
              glow: parseFloat(container.getAttribute('data-aura-flow-glow')) || fluidConfig.glow,
              electric: parseFloat(container.getAttribute('data-aura-flow-electric')) || fluidConfig.electric,
              radius: parseFloat(container.getAttribute('data-aura-flow-radius')) || fluidConfig.radius,
              speed: parseFloat(container.getAttribute('data-aura-flow-speed')) || fluidConfig.speed,
              pulseSpeed: parseFloat(container.getAttribute('data-aura-flow-pulse-speed')) || fluidConfig.pulseSpeed
            };
          }
          
          _setup(container) {
            if (getComputedStyle(container).position === 'static') {
              container.style.position = 'relative';
            }
            
            this.wrapper = document.createElement('div');
            Object.assign(this.wrapper.style, {
              position: 'absolute',
              top: '0',
              left: '0',
              width: '100%',
              height: '100%',
              zIndex: '1',
              pointerEvents: 'none',
              opacity: this.config.opacity.toString()
            });
            
            this.canvas = document.createElement('canvas');
            Object.assign(this.canvas.style, {
              position: 'absolute',
              top: '0',
              left: '0',
              width: '100%',
              height: '100%',
              mixBlendMode: 'screen',
              pointerEvents: 'none'
            });
            
            this.wrapper.appendChild(this.canvas);
            container.insertBefore(this.wrapper, container.firstChild);
          }
          
          _initWebGL() {
            this.gl = this.canvas.getContext('webgl', {
              premultipliedAlpha: false,
              alpha: true
            });
            
            if (!this.gl) {
              console.error('WebGL not supported');
              return;
            }
            
            this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
            this.startTime = Date.now();
            this.vertices = new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
            
            // Initial resize to set canvas dimensions
            this._resize();
          }
          
          _setupShaders() {
            const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
            this.gl.shaderSource(vertexShader, `
              attribute vec2 position;
              varying vec2 uv;
              void main() {
                uv = position * 0.5 + 0.5;
                gl_Position = vec4(position, 0.0, 1.0);
              }
            `);
            this.gl.compileShader(vertexShader);
            
            const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
            this.gl.shaderSource(fragmentShader, `
              precision highp float;
              varying vec2 uv;
              uniform vec2 resolution;
              uniform vec2 mouse;
              uniform float time;
              uniform vec2 lastMouse;
              uniform float isMouseDown;
              uniform vec3 color1;
              uniform vec3 color2;
              uniform vec3 color3;
              uniform float intensity;
              uniform float glow;
              uniform float electric;
              uniform float radius;
              uniform float pulseSpeed;
  
              float lightning(vec2 p, vec2 a, vec2 b) {
                vec2 pa = p - a;
                vec2 ba = b - a;
                float h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0);
                float d = length(pa - ba*h);
                float strength = 1.0 - smoothstep(0.0, 0.02 / electric, d);
                strength *= sin(time * pulseSpeed + p.x * 10.0) * 0.5 + 0.5;
                return strength;
              }
  
              void main() {
                vec2 p = (uv * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
                vec2 m = (mouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
                vec2 lm = (lastMouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
                
                vec3 color = vec3(0.0);
                vec2 mouseVel = (m - lm) * isMouseDown;
                float mouseLen = length(mouseVel);
                
                for(float i = 0.0; i < 3.0; i++) {
                  vec2 pos = p - m;
                  float dist = length(pos);
                  float a = atan(pos.y, pos.x) + sin(time * 0.5) * 2.0;
                  
                  float h = dist * (1.0 + mouseLen * 2.0);
                  vec3 col = mix(
                    mix(color1, color2, i / 2.0),
                    color3,
                    sin(time * 0.5 + h) * 0.5 + 0.5
                  );
                  
                  color += col * (0.15 * intensity / abs(dist - mouseLen * 2.0));
                  color += col * (0.15 * intensity / abs(dist - mouseLen));
                }
  
                float glowStr = exp(-length(p - m) * (1.5 / glow));
                color += mix(color1, color2, 0.5) * glowStr * 0.8;
  
                if (mouseLen > 0.001) {
                  float electricEffect = lightning(p, lm, m);
                  electricEffect += lightning(p, lm + vec2(0.1), m + vec2(-0.1));
                  electricEffect += lightning(p, lm - vec2(0.1), m + vec2(0.1));
                  color += mix(color2, color3, 0.5) * electricEffect * electric;
                }
  
                float pulse = sin(time * pulseSpeed) * 0.5 + 0.5;
                float mouseGlow = exp(-length(p - m) * (2.0 + pulse * 2.0));
                color += mix(color1, color3, pulse) * mouseGlow * pulse * intensity;
  
                color = pow(color, vec3(0.8));
                float alpha = min(1.0, length(color) * 0.7);
                gl_FragColor = vec4(color, alpha);
              }
            `);
            this.gl.compileShader(fragmentShader);
            
            // Check for shader compile errors
            if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
              console.error('Vertex shader compile error:', this.gl.getShaderInfoLog(vertexShader));
              return;
            }
            
            if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
              console.error('Fragment shader compile error:', this.gl.getShaderInfoLog(fragmentShader));
              return;
            }
            
            this.program = this.gl.createProgram();
            this.gl.attachShader(this.program, vertexShader);
            this.gl.attachShader(this.program, fragmentShader);
            this.gl.linkProgram(this.program);
            
            if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
              console.error('Program link error:', this.gl.getProgramInfoLog(this.program));
              return;
            }
            
            this.gl.useProgram(this.program);
            
            const buffer = this.gl.createBuffer();
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
            this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertices, this.gl.STATIC_DRAW);
            
            const positionLocation = this.gl.getAttribLocation(this.program, 'position');
            this.gl.enableVertexAttribArray(positionLocation);
            this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0);
            
            this.uniformLocations = {};
            ['resolution', 'mouse', 'time', 'lastMouse', 'isMouseDown', 
             'color1', 'color2', 'color3', 'intensity', 'glow', 
             'electric', 'radius', 'pulseSpeed'].forEach(name => {
              this.uniformLocations[name] = this.gl.getUniformLocation(this.program, name);
            });
          }
          
          _setupEvents() {
            this.resizeHandler = this._resize.bind(this);
            window.addEventListener('resize', this.resizeHandler);
            
            const updatePosition = (e, touch = false) => {
              const rect = this.container.getBoundingClientRect();
              const clientX = touch ? e.touches[0].clientX : e.clientX;
              const clientY = touch ? e.touches[0].clientY : e.clientY;
              
              if (clientX < rect.left || clientX > rect.right || 
                  clientY < rect.top || clientY > rect.bottom) {
                this._cache.autonomous = true;
                return;
              }
              
              this._cache.autonomous = false;
              this.targetX = (clientX - rect.left) / rect.width;
              this.targetY = 1.0 - (clientY - rect.top) / rect.height;
              
              clearTimeout(this.autonomousTimeout);
              this.autonomousTimeout = setTimeout(() => {
                this._cache.autonomous = true;
              }, 2000);
            };
            
            this.mouseMoveHandler = e => updatePosition(e);
            this.mouseDownHandler = e => {
              const rect = this.container.getBoundingClientRect();
              if (e.clientX >= rect.left && e.clientX <= rect.right && 
                  e.clientY >= rect.top && e.clientY <= rect.bottom) {
                this.isMouseDown = true;
              }
            };
            this.mouseUpHandler = () => this.isMouseDown = false;
            this.mouseLeaveHandler = () => this._cache.autonomous = true;
            
            this.container.addEventListener('mousemove', this.mouseMoveHandler);
            this.container.addEventListener('mousedown', this.mouseDownHandler);
            window.addEventListener('mouseup', this.mouseUpHandler);
            this.container.addEventListener('mouseleave', this.mouseLeaveHandler);
            
            this.touchStartHandler = e => {
              this.isMouseDown = true;
              updatePosition(e, true);
            };
            
            this.touchMoveHandler = e => {
              if (this.isMouseDown) {
                updatePosition(e, true);
              }
            };
            
            this.touchEndHandler = () => {
              this.isMouseDown = false;
              this._cache.autonomous = true;
            };
            
            this.container.addEventListener('touchstart', this.touchStartHandler, {passive: true});
            this.container.addEventListener('touchmove', this.touchMoveHandler, {passive: true});
            this.container.addEventListener('touchend', this.touchEndHandler, {passive: true});
          }
          
          _updateAutonomousPosition(time) {
            if (this._cache.autonomous) {
              const radius = 0.3;
              const speed = 0.5 * this.config.speed;
              this.targetX = 0.5 + Math.cos(time * speed) * radius;
              this.targetY = 0.5 + Math.sin(time * speed * 1.5) * radius;
            }
            
            this.lastX = this.mouseX;
            this.lastY = this.mouseY;
            
            this.mouseX += (this.targetX - this.mouseX) * this.transitionSpeed;
            this.mouseY += (this.targetY - this.mouseY) * this.transitionSpeed;
          }
          
          _resize() {
            const width = this.canvas.clientWidth;
            const height = this.canvas.clientHeight;
            
            if (this.canvas.width !== width || this.canvas.height !== height) {
              this.canvas.width = width;
              this.canvas.height = height;
              this.gl.viewport(0, 0, width, height);
            }
          }
          
          _animate(timestamp = 0) {
            if (timestamp - this._lastFrameTime < this._frameInterval) {
              this.animationFrame = requestAnimationFrame(this._animate.bind(this));
              return;
            }
            
            this._lastFrameTime = timestamp;
            this._resize();
            
            const time = (Date.now() - this.startTime) * 0.001 * this.config.speed;
            
            this._updateAutonomousPosition(time);
            
            this.gl.enable(this.gl.BLEND);
            this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
            
            this.gl.uniform2f(this.uniformLocations.resolution, this.canvas.width, this.canvas.height);
            this.gl.uniform2f(this.uniformLocations.mouse, this.mouseX, this.mouseY);
            this.gl.uniform2f(this.uniformLocations.lastMouse, this.lastX, this.lastY);
            this.gl.uniform1f(this.uniformLocations.time, time);
            this.gl.uniform1f(this.uniformLocations.isMouseDown, this.isMouseDown ? 1.0 : 0.0);
            
            this.gl.uniform3fv(this.uniformLocations.color1, this.config.colors[0]);
            this.gl.uniform3fv(this.uniformLocations.color2, this.config.colors[1]);
            this.gl.uniform3fv(this.uniformLocations.color3, this.config.colors[2]);
            
            this.gl.uniform1f(this.uniformLocations.intensity, this.config.intensity);
            this.gl.uniform1f(this.uniformLocations.glow, this.config.glow);
            this.gl.uniform1f(this.uniformLocations.electric, this.config.electric);
            this.gl.uniform1f(this.uniformLocations.radius, this.config.radius);
            this.gl.uniform1f(this.uniformLocations.pulseSpeed, this.config.pulseSpeed);
            
            this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
            
            this.animationFrame = requestAnimationFrame(this._animate.bind(this));
          }
          
          destroy() {
            if (this.animationFrame) {
              cancelAnimationFrame(this.animationFrame);
            }
            
            if (this.autonomousTimeout) {
              clearTimeout(this.autonomousTimeout);
            }
            
            window.removeEventListener('resize', this.resizeHandler);
            
            this.container.removeEventListener('mousemove', this.mouseMoveHandler);
            this.container.removeEventListener('mousedown', this.mouseDownHandler);
            window.removeEventListener('mouseup', this.mouseUpHandler);
            this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
            
            this.container.removeEventListener('touchstart', this.touchStartHandler);
            this.container.removeEventListener('touchmove', this.touchMoveHandler);
            this.container.removeEventListener('touchend', this.touchEndHandler);
            
            if (this.gl) {
              this.gl.deleteProgram(this.program);
              const ext = this.gl.getExtension('WEBGL_lose_context');
              if (ext) ext.loseContext();
            }
            
            if (this.wrapper && this.wrapper.parentNode) {
              this.wrapper.parentNode.removeChild(this.wrapper);
            }
          }
        }
        
        // Function to generate JavaScript code
        function generateJavaScriptCode() {
          return `(function() {
  'use strict';

  // Configuration object for the Aura Flow animation (customized from your settings)
  const DEFAULT_CONFIG = {
    opacity: ${fluidConfig.opacity},
    intensity: ${fluidConfig.intensity},
    glow: ${fluidConfig.glow},
    electric: ${fluidConfig.electric},
    radius: ${fluidConfig.radius},
    speed: ${fluidConfig.speed},
    pulseSpeed: ${fluidConfig.pulseSpeed},
    transition: ${fluidConfig.transition},
    colors: [
      '${fluidConfig.colors[0]}',
      '${fluidConfig.colors[1]}',
      '${fluidConfig.colors[2]}'
    ]
  };

  // Function to convert hex to RGB array (0-1 range for WebGL)
  function hexToRGB(hex) {
    try {
      if (!hex || typeof hex !== 'string') {
        console.error('Invalid hex value provided:', hex);
        return [0.5, 0.5, 1.0]; // Default blue color
      }
      
      // Make sure hex starts with #
      if (hex.charAt(0) !== '#') {
        hex = '#' + hex;
      }
      
      const shorthand = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;
      hex = hex.replace(shorthand, (m, r, g, b) => {
        return '#' + r + r + g + g + b + b;
      });
      
      const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);
      if (!result) {
        console.error('Could not parse hex value:', hex);
        return [0.5, 0.5, 1.0]; // Default blue color
      }
      
      const r = parseInt(result[1], 16) / 255;
      const g = parseInt(result[2], 16) / 255;
      const b = parseInt(result[3], 16) / 255;
      
      return [r, g, b];
    } catch (error) {
      console.error('Error in hexToRGB:', error, 'for hex value:', hex);
      return [0.5, 0.5, 1.0]; // Default blue color
    }
  }

  // FluidAnimation class implementation
  class FluidAnimation {
    constructor(container) {
      this.container = container;
      this._cache = {
        dimensions: { width: 0, height: 0, aspect: 0 },
        mouse: { current: { x: 0.5, y: 0.5 }, last: { x: 0.5, y: 0.5 } },
        time: 0,
        frameCount: 0,
        autonomous: true
      };
      
      this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
      this._frameInterval = this.isMobile ? 1000/30 : 1000/60;
      this._lastFrameTime = 0;
      this.mouseX = 0.5;
      this.mouseY = 0.5;
      this.lastX = 0.5;
      this.lastY = 0.5;
      this.targetX = 0.5;
      this.targetY = 0.5;
      this.transitionSpeed = parseFloat(container.getAttribute('data-aura-flow-transition')) || DEFAULT_CONFIG.transition;
      this.isMouseDown = false;
      
      this.config = this._parseConfig(container);
      this._setup(container);
      this._initWebGL();
      this._setupShaders();
      this._setupEvents();
      this._animate();
    }
    
    _parseConfig(container) {
      // Extract colors from data attributes or use defaults
      const colors = [];
      
      for (let i = 1; i <= 3; i++) {
        const colorAttr = 'data-aura-flow-color' + i;
        if (container.hasAttribute(colorAttr)) {
          const hexColor = container.getAttribute(colorAttr);
          colors.push(hexToRGB(hexColor));
        }
      }
      
      // If no colors specified, use default colors
      if (colors.length !== 3) {
        colors.length = 0; // Clear the array
        colors.push(hexToRGB(DEFAULT_CONFIG.colors[0]));
        colors.push(hexToRGB(DEFAULT_CONFIG.colors[1]));
        colors.push(hexToRGB(DEFAULT_CONFIG.colors[2]));
      }
      
      return {
        colors: colors,
        opacity: parseFloat(container.getAttribute('data-aura-flow-opacity')) || DEFAULT_CONFIG.opacity,
        intensity: parseFloat(container.getAttribute('data-aura-flow-intensity')) || DEFAULT_CONFIG.intensity,
        glow: parseFloat(container.getAttribute('data-aura-flow-glow')) || DEFAULT_CONFIG.glow,
        electric: parseFloat(container.getAttribute('data-aura-flow-electric')) || DEFAULT_CONFIG.electric,
        radius: parseFloat(container.getAttribute('data-aura-flow-radius')) || DEFAULT_CONFIG.radius,
        speed: parseFloat(container.getAttribute('data-aura-flow-speed')) || DEFAULT_CONFIG.speed,
        pulseSpeed: parseFloat(container.getAttribute('data-aura-flow-pulse-speed')) || DEFAULT_CONFIG.pulseSpeed
      };
    }
    
    _setup(container) {
      if (getComputedStyle(container).position === 'static') {
        container.style.position = 'relative';
      }
      
      this.wrapper = document.createElement('div');
      Object.assign(this.wrapper.style, {
        position: 'absolute',
        top: '0',
        left: '0',
        width: '100%',
        height: '100%',
        zIndex: '1',
        pointerEvents: 'none',
        opacity: this.config.opacity.toString()
      });
      
      this.canvas = document.createElement('canvas');
      Object.assign(this.canvas.style, {
        position: 'absolute',
        top: '0',
        left: '0',
        width: '100%',
        height: '100%',
        mixBlendMode: 'screen',
        pointerEvents: 'none'
      });
      
      this.wrapper.appendChild(this.canvas);
      container.insertBefore(this.wrapper, container.firstChild);
    }
    
    _initWebGL() {
      this.gl = this.canvas.getContext('webgl', {
        premultipliedAlpha: false,
        alpha: true
      });
      
      if (!this.gl) {
        console.error('WebGL not supported');
        return;
      }
      
      this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
      this.startTime = Date.now();
      this.vertices = new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
      
      // Initial resize to set canvas dimensions
      this._resize();
    }
    
    _setupShaders() {
      const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
      this.gl.shaderSource(vertexShader, \`
        attribute vec2 position;
        varying vec2 uv;
        void main() {
          uv = position * 0.5 + 0.5;
          gl_Position = vec4(position, 0.0, 1.0);
        }
      \`);
      this.gl.compileShader(vertexShader);
      
      const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
      this.gl.shaderSource(fragmentShader, \`
        precision highp float;
        varying vec2 uv;
        uniform vec2 resolution;
        uniform vec2 mouse;
        uniform float time;
        uniform vec2 lastMouse;
        uniform float isMouseDown;
        uniform vec3 color1;
        uniform vec3 color2;
        uniform vec3 color3;
        uniform float intensity;
        uniform float glow;
        uniform float electric;
        uniform float radius;
        uniform float pulseSpeed;

        float lightning(vec2 p, vec2 a, vec2 b) {
          vec2 pa = p - a;
          vec2 ba = b - a;
          float h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0);
          float d = length(pa - ba*h);
          float strength = 1.0 - smoothstep(0.0, 0.02 / electric, d);
          strength *= sin(time * pulseSpeed + p.x * 10.0) * 0.5 + 0.5;
          return strength;
        }

        void main() {
          vec2 p = (uv * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
          vec2 m = (mouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
          vec2 lm = (lastMouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
          
          vec3 color = vec3(0.0);
          vec2 mouseVel = (m - lm) * isMouseDown;
          float mouseLen = length(mouseVel);
          
          for(float i = 0.0; i < 3.0; i++) {
            vec2 pos = p - m;
            float dist = length(pos);
            float a = atan(pos.y, pos.x) + sin(time * 0.5) * 2.0;
            
            float h = dist * (1.0 + mouseLen * 2.0);
            vec3 col = mix(
              mix(color1, color2, i / 2.0),
              color3,
              sin(time * 0.5 + h) * 0.5 + 0.5
            );
            
            color += col * (0.15 * intensity / abs(dist - mouseLen * 2.0));
            color += col * (0.15 * intensity / abs(dist - mouseLen));
          }

          float glowStr = exp(-length(p - m) * (1.5 / glow));
          color += mix(color1, color2, 0.5) * glowStr * 0.8;

          if (mouseLen > 0.001) {
            float electricEffect = lightning(p, lm, m);
            electricEffect += lightning(p, lm + vec2(0.1), m + vec2(-0.1));
            electricEffect += lightning(p, lm - vec2(0.1), m + vec2(0.1));
            color += mix(color2, color3, 0.5) * electricEffect * electric;
          }

          float pulse = sin(time * pulseSpeed) * 0.5 + 0.5;
          float mouseGlow = exp(-length(p - m) * (2.0 + pulse * 2.0));
          color += mix(color1, color3, pulse) * mouseGlow * pulse * intensity;

          color = pow(color, vec3(0.8));
          float alpha = min(1.0, length(color) * 0.7);
          gl_FragColor = vec4(color, alpha);
        }
      \`);
      this.gl.compileShader(fragmentShader);
      
      // Check for shader compile errors
      if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
        console.error('Vertex shader compile error:', this.gl.getShaderInfoLog(vertexShader));
        return;
      }
      
      if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
        console.error('Fragment shader compile error:', this.gl.getShaderInfoLog(fragmentShader));
        return;
      }
      
      this.program = this.gl.createProgram();
      this.gl.attachShader(this.program, vertexShader);
      this.gl.attachShader(this.program, fragmentShader);
      this.gl.linkProgram(this.program);
      
      if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
        console.error('Program link error:', this.gl.getProgramInfoLog(this.program));
        return;
      }
      
      this.gl.useProgram(this.program);
      
      const buffer = this.gl.createBuffer();
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
      this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertices, this.gl.STATIC_DRAW);
      
      const positionLocation = this.gl.getAttribLocation(this.program, 'position');
      this.gl.enableVertexAttribArray(positionLocation);
      this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0);
      
      this.uniformLocations = {};
      ['resolution', 'mouse', 'time', 'lastMouse', 'isMouseDown', 
       'color1', 'color2', 'color3', 'intensity', 'glow', 
       'electric', 'radius', 'pulseSpeed'].forEach(name => {
        this.uniformLocations[name] = this.gl.getUniformLocation(this.program, name);
      });
    }
    
    _setupEvents() {
      this.resizeHandler = this._resize.bind(this);
      window.addEventListener('resize', this.resizeHandler);
      
      const updatePosition = (e, touch = false) => {
        const rect = this.container.getBoundingClientRect();
        const clientX = touch ? e.touches[0].clientX : e.clientX;
        const clientY = touch ? e.touches[0].clientY : e.clientY;
        
        if (clientX < rect.left || clientX > rect.right || 
            clientY < rect.top || clientY > rect.bottom) {
          this._cache.autonomous = true;
          return;
        }
        
        this._cache.autonomous = false;
        this.targetX = (clientX - rect.left) / rect.width;
        this.targetY = 1.0 - (clientY - rect.top) / rect.height;
        
        clearTimeout(this.autonomousTimeout);
        this.autonomousTimeout = setTimeout(() => {
          this._cache.autonomous = true;
        }, 2000);
      };
      
      this.mouseMoveHandler = e => updatePosition(e);
      this.mouseDownHandler = e => {
        const rect = this.container.getBoundingClientRect();
        if (e.clientX >= rect.left && e.clientX <= rect.right && 
            e.clientY >= rect.top && e.clientY <= rect.bottom) {
          this.isMouseDown = true;
        }
      };
      this.mouseUpHandler = () => this.isMouseDown = false;
      this.mouseLeaveHandler = () => this._cache.autonomous = true;
      
      this.container.addEventListener('mousemove', this.mouseMoveHandler);
      this.container.addEventListener('mousedown', this.mouseDownHandler);
      window.addEventListener('mouseup', this.mouseUpHandler);
      this.container.addEventListener('mouseleave', this.mouseLeaveHandler);
      
      this.touchStartHandler = e => {
        this.isMouseDown = true;
        updatePosition(e, true);
      };
      
      this.touchMoveHandler = e => {
        if (this.isMouseDown) {
          updatePosition(e, true);
        }
      };
      
      this.touchEndHandler = () => {
        this.isMouseDown = false;
        this._cache.autonomous = true;
      };
      
      this.container.addEventListener('touchstart', this.touchStartHandler, {passive: true});
      this.container.addEventListener('touchmove', this.touchMoveHandler, {passive: true});
      this.container.addEventListener('touchend', this.touchEndHandler, {passive: true});
    }
    
    _updateAutonomousPosition(time) {
      if (this._cache.autonomous) {
        const radius = 0.3;
        const speed = 0.5 * this.config.speed;
        this.targetX = 0.5 + Math.cos(time * speed) * radius;
        this.targetY = 0.5 + Math.sin(time * speed * 1.5) * radius;
      }
      
      this.lastX = this.mouseX;
      this.lastY = this.mouseY;
      
      this.mouseX += (this.targetX - this.mouseX) * this.transitionSpeed;
      this.mouseY += (this.targetY - this.mouseY) * this.transitionSpeed;
    }
    
    _resize() {
      const width = this.canvas.clientWidth;
      const height = this.canvas.clientHeight;
      
      if (this.canvas.width !== width || this.canvas.height !== height) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.gl.viewport(0, 0, width, height);
      }
    }
    
    _animate(timestamp = 0) {
      if (timestamp - this._lastFrameTime < this._frameInterval) {
        this.animationFrame = requestAnimationFrame(this._animate.bind(this));
        return;
      }
      
      this._lastFrameTime = timestamp;
      this._resize();
      
      const time = (Date.now() - this.startTime) * 0.001 * this.config.speed;
      
      this._updateAutonomousPosition(time);
      
      this.gl.enable(this.gl.BLEND);
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
      
      this.gl.uniform2f(this.uniformLocations.resolution, this.canvas.width, this.canvas.height);
      this.gl.uniform2f(this.uniformLocations.mouse, this.mouseX, this.mouseY);
      this.gl.uniform2f(this.uniformLocations.lastMouse, this.lastX, this.lastY);
      this.gl.uniform1f(this.uniformLocations.time, time);
      this.gl.uniform1f(this.uniformLocations.isMouseDown, this.isMouseDown ? 1.0 : 0.0);
      
      this.gl.uniform3fv(this.uniformLocations.color1, this.config.colors[0]);
      this.gl.uniform3fv(this.uniformLocations.color2, this.config.colors[1]);
      this.gl.uniform3fv(this.uniformLocations.color3, this.config.colors[2]);
      
      this.gl.uniform1f(this.uniformLocations.intensity, this.config.intensity);
      this.gl.uniform1f(this.uniformLocations.glow, this.config.glow);
      this.gl.uniform1f(this.uniformLocations.electric, this.config.electric);
      this.gl.uniform1f(this.uniformLocations.radius, this.config.radius);
      this.gl.uniform1f(this.uniformLocations.pulseSpeed, this.config.pulseSpeed);
      
      this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
      
      this.animationFrame = requestAnimationFrame(this._animate.bind(this));
    }
    
    destroy() {
      if (this.animationFrame) {
        cancelAnimationFrame(this.animationFrame);
      }
      
      if (this.autonomousTimeout) {
        clearTimeout(this.autonomousTimeout);
      }
      
      window.removeEventListener('resize', this.resizeHandler);
      
      this.container.removeEventListener('mousemove', this.mouseMoveHandler);
      this.container.removeEventListener('mousedown', this.mouseDownHandler);
      window.removeEventListener('mouseup', this.mouseUpHandler);
      this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
      
      this.container.removeEventListener('touchstart', this.touchStartHandler);
      this.container.removeEventListener('touchmove', this.touchMoveHandler);
      this.container.removeEventListener('touchend', this.touchEndHandler);
      
      if (this.gl) {
        this.gl.deleteProgram(this.program);
        const ext = this.gl.getExtension('WEBGL_lose_context');
        if (ext) ext.loseContext();
      }
      
      if (this.wrapper && this.wrapper.parentNode) {
        this.wrapper.parentNode.removeChild(this.wrapper);
      }
    }
  }

  // Initialize on DOM content loaded
  document.addEventListener('DOMContentLoaded', () => {
    const fluidElements = new Map();
    
    document.querySelectorAll('[data-aura-flow]').forEach(element => {
      const animation = new FluidAnimation(element);
      fluidElements.set(element, animation);
    });
    
    // Setup mutation observer for dynamically added elements
    const observer = new MutationObserver(mutations => {
      mutations.forEach(mutation => {
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === 1 && node.matches('[data-aura-flow]:not([data-aura-flow-initialized])')) {
            const animation = new FluidAnimation(node);
            fluidElements.set(node, animation);
            node.dataset.auraFlowInitialized = 'true';
          }
        });
        
        mutation.removedNodes.forEach(node => {
          if (node.nodeType === 1 && fluidElements.has(node)) {
            const animation = fluidElements.get(node);
            animation.destroy();
            fluidElements.delete(node);
          }
        });
      });
    });
    
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
    
    // Bricks Builder specific init
    document.addEventListener('bricks-lazy-load', () => {
      document.querySelectorAll('[data-aura-flow]:not([data-aura-flow-initialized])').forEach(element => {
        const animation = new FluidAnimation(element);
        fluidElements.set(element, animation);
        element.dataset.auraFlowInitialized = 'true';
      });
    });
  });
})();

/* 
To use this Aura Flow effect in Bricks Builder:
1. Add this script to a Code element
2. Add the attribute data-aura-flow="true" to any section where you want the effect to be applied
3. Optionally add data-aura-flow-color1="#ff0000", data-aura-flow-color2="#00ff00", etc. to customize colors and other parameters
*/`;
        }
        
        function downloadJsFile() {
          const jsCode = generateJavaScriptCode();
          const blob = new Blob([jsCode], { type: 'application/javascript' });
          const url = URL.createObjectURL(blob);
          
          const a = document.createElement('a');
          a.href = url;
          a.download = 'aura-flow.js';
          a.click();
          
          URL.revokeObjectURL(url);
        }
        
        function initializeUI() {
          // Initialize the preview
          initFluidAnimation();
          
          // Setup color inputs
          const colorInputs = document.querySelectorAll('input[type="color"]');
          colorInputs.forEach(input => {
            const textInput = document.getElementById(input.id + '-text');
            
            // When color picker changes
            input.addEventListener('input', () => {
              if (textInput) {
                textInput.value = input.value;
              }
              updateColorConfiguration();
            });
            
            // When text input changes
            if (textInput) {
              textInput.addEventListener('input', () => {
                const isValidHex = /^#[0-9A-F]{6}$/i.test(textInput.value);
                if (isValidHex) {
                  input.value = textInput.value;
                  updateColorConfiguration();
                }
              });
              
              textInput.addEventListener('blur', () => {
                const isValidHex = /^#[0-9A-F]{6}$/i.test(textInput.value);
                if (!isValidHex) {
                  textInput.value = input.value;
                } else {
                  updateColorConfiguration();
                }
              });
            }
          });
          
          // Setup range inputs
          const rangeInputs = document.querySelectorAll('input[type="range"]');
          rangeInputs.forEach(input => {
            const valueElement = document.getElementById(`${input.id}-value`);
            
            // Set initial value
            if (valueElement) {
              valueElement.textContent = input.value;
            }
            
            input.addEventListener('input', () => {
              if (valueElement) {
                valueElement.textContent = input.value;
              }
              
              // Update configuration based on the slider
              switch (input.id) {
                case 'opacity':
                  fluidConfig.opacity = parseFloat(input.value);
                  break;
                case 'intensity':
                  fluidConfig.intensity = parseFloat(input.value);
                  break;
                case 'glow':
                  fluidConfig.glow = parseFloat(input.value);
                  break;
                case 'electric':
                  fluidConfig.electric = parseFloat(input.value);
                  break;
                case 'radius':
                  fluidConfig.radius = parseFloat(input.value);
                  break;
                case 'speed':
                  fluidConfig.speed = parseFloat(input.value);
                  break;
                case 'pulse-speed':
                  fluidConfig.pulseSpeed = parseFloat(input.value);
                  break;
                case 'transition':
                  fluidConfig.transition = parseFloat(input.value);
                  break;
              }
              
              // Update the preview
              initFluidAnimation();
            });
          });
          
          // Setup modal and download functionality
          const downloadBtn = document.getElementById('download-btn');
          const codeModal = document.getElementById('code-modal');
          const closeModal = document.getElementById('close-modal');
          const copyBtn = document.getElementById('copy-btn');
          const downloadFileBtn = document.getElementById('download-file-btn');
          
          if (downloadBtn) {
            downloadBtn.addEventListener('click', () => {
              const jsCode = generateJavaScriptCode();
              document.getElementById('js-code').textContent = jsCode;
              codeModal.classList.add('active');
            });
          }
          
          if (closeModal) {
            closeModal.addEventListener('click', () => {
              codeModal.classList.remove('active');
            });
          }
          
          if (copyBtn) {
            copyBtn.addEventListener('click', () => {
              const jsCode = generateJavaScriptCode();
              navigator.clipboard.writeText(jsCode)
                .then(() => {
                  copyBtn.textContent = 'Copied!';
                  setTimeout(() => {
                    copyBtn.textContent = 'Copy to Clipboard';
                  }, 2000);
                })
                .catch(err => {
                  console.error('Could not copy text: ', err);
                });
            });
          }
          
          if (downloadFileBtn) {
            downloadFileBtn.addEventListener('click', downloadJsFile);
          }
          
          if (codeModal) {
            codeModal.addEventListener('click', (e) => {
              if (e.target === codeModal) {
                codeModal.classList.remove('active');
              }
            });
          }
        }
        
        function updateColorConfiguration() {
          // Update the colors in the configuration
          const colors = [];
          for (let i = 1; i <= 3; i++) {
            colors.push(document.getElementById('color' + i).value);
          }
          
          fluidConfig.colors = colors;
          
          // Update the preview
          initFluidAnimation();
        }
        
        // Function to handle copyable attributes
        function copyAttributeToClipboard(attribute, element) {
          // Remover el valor "=true" del atributo si existe
          const cleanAttribute = attribute.split('=')[0];
          
          navigator.clipboard.writeText(cleanAttribute)
            .then(() => {
              // Añadir animación directamente sobre el elemento copiado
              if (element) {
                // Guardar el texto original
                const originalText = element.innerHTML;
                
                // Crear un span para el mensaje de "Copied!"
                const copiedSpan = document.createElement('span');
                copiedSpan.textContent = "Copied!";
                copiedSpan.style.cssText = 'position:absolute; top:0; left:0; width:100%; height:100%; display:flex; align-items:center; justify-content:center; background-color:rgba(239, 96, 19, 0.9); color:white; border-radius:4px; font-weight:bold; animation: fadeInOut 1.5s ease forwards;';
                
                // Añadir estilos de animación
                const style = document.createElement('style');
                style.textContent = `
                  @keyframes fadeInOut {
                    0% { opacity: 0; }
                    10% { opacity: 1; }
                    80% { opacity: 1; }
                    100% { opacity: 0; }
                  }
                `;
                document.head.appendChild(style);
                
                // Asegurar que el elemento tenga posición relativa para el posicionamiento absoluto
                element.style.position = 'relative';
                element.style.overflow = 'hidden';
                
                // Añadir el span al elemento
                element.appendChild(copiedSpan);
                
                // Eliminar el span después de que termine la animación
                setTimeout(() => {
                  element.removeChild(copiedSpan);
                  document.head.removeChild(style);
                }, 1500);
              }
              
              // Animación mejorada de feedback visual adicional (tooltip)
              const tooltip = document.createElement('div');
              tooltip.textContent = 'Copied to clipboard!';
              tooltip.style.cssText = 'position:fixed;padding:8px 16px;background:#333;color:white;border-radius:6px;font-size:14px;z-index:9999;transition:all 0.3s ease;opacity:0;transform:translateY(10px);box-shadow:0 4px 12px rgba(0,0,0,0.2);';
              document.body.appendChild(tooltip);
              
              // Position near cursor
              tooltip.style.top = (event.clientY + 20) + 'px';
              tooltip.style.left = (event.clientX + 10) + 'px';
              
              // Show with animation
              setTimeout(() => {
                tooltip.style.opacity = '1';
                tooltip.style.transform = 'translateY(0)';
                
                // Hide with animation
                setTimeout(() => {
                  tooltip.style.opacity = '0';
                  tooltip.style.transform = 'translateY(-10px)';
                  
                  // Remove from DOM
                  setTimeout(() => {
                    document.body.removeChild(tooltip);
                  }, 300);
                }, 1500);
              }, 10);
            })
            .catch(err => {
              console.error('Error copying: ', err);
            });
        }
        
        // Add event listeners for copyable elements
        document.querySelectorAll('.copy-attribute').forEach(el => {
          el.addEventListener('click', function(e) {
            e.preventDefault();
            const attribute = this.getAttribute('data-attribute');
            copyAttributeToClipboard(attribute, this);
          });
        });
        
        // Initialize the UI
        initializeUI();
      });
    </script>
    </body>
    </html>