From a05e9bf10bad1f8f770c6eb2cfcf5d73feaf6845 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 10 Jan 2026 20:21:59 +0000 Subject: [PATCH] Initial version of lava lamp generator --- assets/sass/pages/resources.scss | 29 +- content/resources/adoptables/index.md | 74 ++ content/resources/lavalamp/index.md | 19 +- .../shortcodes/button-generator.html.backup | 607 -------------- layouts/shortcodes/lavalamp-adoptable.html | 755 ++++++++++++++++++ static/js/adoptables/lavalamp.js | 364 +++++++++ static/lavalamp-demo.html | 371 +++++++++ 7 files changed, 1582 insertions(+), 637 deletions(-) create mode 100644 content/resources/adoptables/index.md delete mode 100644 layouts/shortcodes/button-generator.html.backup create mode 100644 layouts/shortcodes/lavalamp-adoptable.html create mode 100644 static/js/adoptables/lavalamp.js create mode 100644 static/lavalamp-demo.html diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index e16bafe..ac1d762 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -754,12 +754,12 @@ &::before { content: ""; position: absolute; - left: -2rem; + left: -1rem; top: 50%; transform: translateY(-50%); - width: 3rem; - height: 3px; - background: linear-gradient(90deg, transparent, #0096ff); + width: 5px; + height: 100%; + background: linear-gradient(180deg, transparent, #0096ff, transparent); //box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); } @@ -767,12 +767,12 @@ &::after { content: ""; position: absolute; - right: -2rem; + right: -1rem; top: 50%; transform: translateY(-50%); - width: 3rem; - height: 3px; - background: linear-gradient(90deg, #ff7800, transparent); + width: 5px; + height: 100%; + background: linear-gradient(180deg, transparent, #ff7800, transparent); //box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); } @@ -785,19 +785,6 @@ @include media-up(md) { font-size: 3rem; - - &::before, - &::after { - width: 6rem; - } - - &::before { - left: -2rem; - } - - &::after { - right: -2rem; - } } } diff --git a/content/resources/adoptables/index.md b/content/resources/adoptables/index.md new file mode 100644 index 0000000..b2bffcc --- /dev/null +++ b/content/resources/adoptables/index.md @@ -0,0 +1,74 @@ +--- +title: "Adoptables" +date: 2026-01-10 +description: "Customisable widgets and animations for your website" +icon: "adoptables" +draft: false +--- + +Welcome to my adoptables collection! These are customisable widgets you can embed on your own website. Each adoptable comes with a live preview and customisation options. Just configure it how you like, grab the code, and paste it into your site. + +All adoptables are: + +- **Free to use** - No attribution required (but appreciated!) +- **Probably customisable** - Tweak your own colors and settings if applicable +- **Self-contained** - Single script tag, no dependencies +- **Responsive** - Scales to fit your layout + +--- + +## Lava Lamp + +My brother had a blue and yellow lava lamp when I was younger and I was obsessed with it, so making one for my website was an obvious choice... and now I'd like to share it with everyone! + +Using the pixelation effect with very high contrast colours is going to result in an aura on the blobs due to the way the SVG filter works, I am trying to find a solution for this. + +{{< lavalamp-adoptable >}} + +--- + +## Usage Instructions + +1. **Customise** - Use the controls to adjust colors, animation speed, and other settings +2. **Get Code** - Click "Get Embed Code" to generate your custom script tag +3. **Embed** - Copy the code and paste it anywhere in your HTML +4. **Style** - Wrap it in a div and set width/height to control the lamp size + +### Example Container + +The lava lamp will scale to fit its container. You can control the size like this: + +```html +
+ +
+``` + +### Customisation Options + +- `data-bg-color-1` & `data-bg-color-2`: Background gradient colors +- `data-blob-color-1` & `data-blob-color-2`: Blob gradient colors +- `data-case-color`: Color for the top cap and bottom base +- `data-blob-count`: Number of blobs (3-12) +- `data-speed`: Animation speed multiplier (0.5-1.5x) +- `data-blob-size`: Blob size multiplier (0.5-2.0x) +- `data-pixelate`: Set to "true" for a retro pixelated effect +- `data-pixel-size`: Pixel size for pixelation effect (2-10, default 4) + +--- + +## More Coming Soon + +The companion cube is coming soon! + +Got ideas for adoptables you'd like to see? Let me know! diff --git a/content/resources/lavalamp/index.md b/content/resources/lavalamp/index.md index e18dc73..66b2243 100644 --- a/content/resources/lavalamp/index.md +++ b/content/resources/lavalamp/index.md @@ -5,22 +5,23 @@ description: "A pure CSS lavalamp animation with floating blobs" icon: "lavalamp" demo_url: "" source_url: "" -draft: true +draft: false --- -A mesmerizing lavalamp effect created entirely with HTML and CSS. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. +A mesmerizing lavalamp effect created with HTML, CSS, and JavaScript. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. + +## Want Your Own Customizable Lava Lamp? + +Check out the [Adoptables page](/resources/adoptables/) where you can customize your own lava lamp with your choice of colors, blob count, and animation speed - then get the embed code to add it to your own website! ## Features -- Pure CSS animation (no JavaScript required) -- Customizable colors and blob shapes - Smooth, organic floating motion +- Customizable colors and blob shapes +- Adjustable animation speed and blob count - Responsive design +- Self-contained embed code ## How It Works -The animation uses CSS keyframes to create the illusion of blobs floating up and down. Multiple blob elements with different animation delays create a realistic lava lamp effect. - -## Usage - -Simply copy the HTML and CSS into your project. You can customize the colors by changing the gradient values and adjust the animation speed by modifying the animation duration. +The animation uses CSS keyframes to create the illusion of blobs floating up and down. Multiple blob elements with different animation delays and paths create a realistic lava lamp effect. An SVG filter creates the "goo" effect that makes the blobs merge together smoothly. diff --git a/layouts/shortcodes/button-generator.html.backup b/layouts/shortcodes/button-generator.html.backup deleted file mode 100644 index 6babacb..0000000 --- a/layouts/shortcodes/button-generator.html.backup +++ /dev/null @@ -1,607 +0,0 @@ -
-
-
-

Preview

-
-
- -
-
- - -
-

Presets

- - - -
-
- -
-
-

- Text Line 1 - -

-
- - - - - - - - - - - - - - - - - -
- - -
- - - - - - - - - -
- - -
-
-
- -
-

- Text Line 1 Animation - -

-
- - - - - -
-
- -
-

- Text Line 2 - -

-
- - - - - - - - - - - - - - - - - -
- - -
- - - - - - - - - -
- - -
-
-
- -
-

- Text Line 2 Animation - -

-
- - - - - -
-
- -
-

- Background - -

-
- - - -
- - -
- - - - -
-
- -
-

- Background Animation - -

-
- - - - -
-
- -
-

- Border - -

-
- - - - - - - - -
-
- -
-

- Special Effects - -

-
-

- Almost all animations should stack, so pick as many as you want. -

- - - - - - - - - - - - - - - - - - {{/* - - - */}} -
-
-
-
-
diff --git a/layouts/shortcodes/lavalamp-adoptable.html b/layouts/shortcodes/lavalamp-adoptable.html new file mode 100644 index 0000000..08de9ad --- /dev/null +++ b/layouts/shortcodes/lavalamp-adoptable.html @@ -0,0 +1,755 @@ +
+
+
+

Preview

+
+
+
+
+
+ 40px × 60px +
+
+
+
+
+ 100px × 200px +
+
+
+
+
+ 200px × 350px +
+
+
+ +
+

Customize Your Lava Lamp

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+ +
+
+
+ + +
+ + + + diff --git a/static/js/adoptables/lavalamp.js b/static/js/adoptables/lavalamp.js new file mode 100644 index 0000000..6c55277 --- /dev/null +++ b/static/js/adoptables/lavalamp.js @@ -0,0 +1,364 @@ +(function () { + "use strict"; + + const currentScript = document.currentScript; + if (!currentScript) { + console.error("Lava Lamp: Could not find current script tag"); + return; + } + + // Read configuration from data attributes with defaults + const config = { + bgColor1: currentScript.dataset["bgColor-1"] || "#F8E45C", + bgColor2: currentScript.dataset["bgColor-2"] || "#FF7800", + blobColor1: currentScript.dataset["blobColor-1"] || "#FF4500", + blobColor2: currentScript.dataset["blobColor-2"] || "#FF6347", + caseColor: currentScript.dataset.caseColor || "#333333", + blobCount: parseInt(currentScript.dataset.blobCount) || 6, + speed: parseFloat(currentScript.dataset.speed) || 1.0, + blobSize: parseFloat(currentScript.dataset.blobSize) || 1.0, + pixelate: currentScript.dataset.pixelate === "true" || false, + pixelSize: parseInt(currentScript.dataset.pixelSize) || 4, + }; + + // Helper function to adjust color brightness + function adjustBrightness(color, percent) { + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) + amt; + const G = ((num >> 8) & 0x00ff) + amt; + const B = (num & 0x0000ff) + amt; + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1) + ); + } + + // Create a host element with shadow DOM for isolation + const host = document.createElement("div"); + host.style.width = "100%"; + host.style.height = "100%"; + host.style.display = "block"; + + if (config.pixelate) { + host.style.overflow = "hidden"; + } + + // Attach shadow DOM + const shadowRoot = host.attachShadow({ mode: "open" }); + + currentScript.parentNode.insertBefore(host, currentScript.nextSibling); + + const gooFilterId = "goo-filter"; + + // Inject CSS into shadow DOM + const style = document.createElement("style"); + style.textContent = ` + .lavalamp-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + ${config.pixelate ? "filter: url(#pixelate-filter);" : ""} + } + + .lamp-cap { + width: 60%; + height: 8%; + flex-shrink: 0; + border-radius: 50% 50% 0 0; + box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.3); + position: relative; + z-index: 10; + } + + .lamp-body { + position: relative; + width: 100%; + flex: 1; + clip-path: polygon(20% 0, 80% 0, 100% 101%, 0% 101%); + overflow: hidden; + } + + .lamp-body::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.15) 20%, + rgba(255, 255, 255, 0.05) 40%, + transparent 60% + ); + pointer-events: none; + } + + .blobs-container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + filter: url(#${gooFilterId}); + pointer-events: none; + z-index: 2; + } + + .blob { + position: absolute; + border-radius: 50%; + animation: lavalamp-float var(--duration) ease-in-out infinite; + opacity: 0.95; + mix-blend-mode: normal; + z-index: 3; + } + + .lamp-base { + width: 100%; + height: 15%; + flex-shrink: 0; + border-radius: 0 0 50% 50%; + box-shadow: + inset 0 2px 5px rgba(255, 255, 255, 0.2), + inset 0 -2px 5px rgba(0, 0, 0, 0.5); + position: relative; + display: flex; + justify-content: center; + align-items: center; + } + + @keyframes lavalamp-float { + 0%, 100% { + transform: translate(var(--start-x), var(--start-y)) scale(1); + } + 25% { + transform: translate(var(--mid1-x), var(--mid1-y)) scale(var(--scale1, 1.1)); + } + 50% { + transform: translate(var(--mid2-x), var(--mid2-y)) scale(var(--scale2, 0.9)); + } + 75% { + transform: translate(var(--mid3-x), var(--mid3-y)) scale(var(--scale3, 1.05)); + } + } + `; + shadowRoot.appendChild(style); + + // Create the HTML structure + const container = document.createElement("div"); + container.className = "lavalamp-container"; + + // SVG filters for goo effect and pixelation + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.style.position = "absolute"; + svg.style.width = "0"; + svg.style.height = "0"; + svg.innerHTML = ` + + + + + + + ${ + config.pixelate + ? ` + + + + + + + + ` + : "" + } + + `; + + const lampCap = document.createElement("div"); + lampCap.className = "lamp-cap"; + lampCap.style.background = `linear-gradient(180deg, ${adjustBrightness(config.caseColor, 40)} 0%, ${config.caseColor} 50%, ${adjustBrightness(config.caseColor, -20)} 100%)`; + + const lampBody = document.createElement("div"); + lampBody.className = "lamp-body"; + lampBody.style.background = `linear-gradient(180deg, ${config.bgColor1} 0%, ${config.bgColor2} 100%)`; + + const blobsContainer = document.createElement("div"); + blobsContainer.className = "blobs-container"; + + const lampBase = document.createElement("div"); + lampBase.className = "lamp-base"; + lampBase.style.background = `linear-gradient(180deg, ${config.caseColor} 0%, ${adjustBrightness(config.caseColor, -20)} 40%, ${adjustBrightness(config.caseColor, -40)} 100%)`; + lampBase.style.borderTop = `1px solid ${adjustBrightness(config.bgColor2, -30)}`; + + // Assemble the structure + lampBody.appendChild(blobsContainer); + container.appendChild(svg); + container.appendChild(lampCap); + container.appendChild(lampBody); + container.appendChild(lampBase); + + // Append to shadow DOM + shadowRoot.appendChild(container); + + // Blob creation and animation + let blobs = []; + + function createBlob() { + const blob = document.createElement("div"); + blob.className = "blob"; + + // Get container dimensions + const containerWidth = lampBody.offsetWidth; + const containerHeight = lampBody.offsetHeight; + + // Size relative to container width (25-40% of width) + const size = + (Math.random() * 0.15 + 0.25) * containerWidth * config.blobSize; + const duration = (Math.random() * 8 + 12) / config.speed; + + // Max X position accounting for blob size and container margins + const maxX = Math.max(0, containerWidth - size); + const startX = maxX > 0 ? Math.random() * maxX : 0; + + // Create gradient for blob + const blobGradient = `radial-gradient(circle at 30% 30%, ${config.blobColor1}, ${config.blobColor2})`; + + blob.style.width = `${size}px`; + blob.style.height = `${size}px`; + blob.style.left = `${startX}px`; + blob.style.bottom = "10px"; + blob.style.position = "absolute"; + blob.style.background = blobGradient; + blob.style.setProperty("--duration", `${duration}s`); + blob.style.setProperty("--start-x", "0px"); + blob.style.setProperty("--start-y", "0px"); + + // Movement waypoints + blob.style.setProperty( + "--mid1-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid1-y", + `${-(Math.random() * 0.15 + 0.25) * containerHeight}px`, + ); + + blob.style.setProperty( + "--mid2-x", + `${(Math.random() * 0.4 - 0.2) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid2-y", + `${-(Math.random() * 0.2 + 0.5) * containerHeight}px`, + ); + + blob.style.setProperty( + "--mid3-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid3-y", + `${-(Math.random() * 0.15 + 0.8) * containerHeight}px`, + ); + + // Scale variations + blob.style.setProperty("--scale1", (Math.random() * 0.3 + 1.0).toFixed(2)); + blob.style.setProperty("--scale2", (Math.random() * 0.3 + 0.85).toFixed(2)); + blob.style.setProperty("--scale3", (Math.random() * 0.3 + 0.95).toFixed(2)); + + // Random delay to stagger animations + blob.style.animationDelay = `${Math.random() * -20}s`; + + return blob; + } + + function updateBlobCount() { + while (blobs.length > config.blobCount) { + const blob = blobs.pop(); + blobsContainer.removeChild(blob); + } + while (blobs.length < config.blobCount) { + const blob = createBlob(); + blobs.push(blob); + blobsContainer.appendChild(blob); + } + } + + // Initialize + function init() { + // Wait for container to have dimensions + // Not sure if this is a great idea, what if this never happens for some reason + // This is as good as I can come up with right now + if (lampBody.offsetWidth === 0 || lampBody.offsetHeight === 0) { + setTimeout(init, 100); + return; + } + + // Scale goo filter blur based on container width + const containerWidth = lampBody.offsetWidth; + + // Apply scaled goo filter + let blurAmount, alphaMultiplier, alphaBias; + + if (containerWidth < 80) { + blurAmount = 3; + alphaMultiplier = 12; + alphaBias = -5; + } else { + blurAmount = Math.max(4, Math.min(7, containerWidth / 20)); + alphaMultiplier = 18; + alphaBias = -7; + } + + const gooFilter = svg.querySelector(`#${gooFilterId} feGaussianBlur`); + if (gooFilter) { + gooFilter.setAttribute("stdDeviation", blurAmount); + } + + const colorMatrix = svg.querySelector(`#${gooFilterId} feColorMatrix`); + if (colorMatrix) { + colorMatrix.setAttribute( + "values", + ` + 1 0 0 0 0 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 ${alphaMultiplier} ${alphaBias} + `, + ); + } + + updateBlobCount(); + } + + // Start when DOM is ready + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/static/lavalamp-demo.html b/static/lavalamp-demo.html new file mode 100644 index 0000000..9da0cdb --- /dev/null +++ b/static/lavalamp-demo.html @@ -0,0 +1,371 @@ + + + + + + Lava Lamp Adoptable - Demo Examples + + + +
+

🌋 Lava Lamp Adoptable Demo

+

+ See the lava lamp in action at various sizes and color schemes! +

+ +
+ +
+
Classic Orange
+
+ +
+
150px × 280px
+
+ + +
+
Purple Dreams
+
+ +
+
120px × 250px
+
+ + +
+
Ocean Blue
+
+ +
+
100px × 200px
+
+ + +
+
Lava Red
+
+ +
+
180px × 320px
+
+ + +
+
Mint Fresh
+
+ +
+
140px × 260px
+
+ + +
+
Sunset Glow
+
+ +
+
110px × 220px
+
+ + +
+
Cyber Green (Pixelated)
+
+ +
+
160px × 300px (6px pixels)
+
+ + +
+
Pink Bubble (Pixelated)
+
+ +
+
130px × 240px (3px pixels)
+
+ + +
+
Tiny Retro (Pixelated)
+
+ +
+
40px × 80px (3px pixels)
+
+
+ +
+

How to Use

+

+ Simply copy and paste this code into your website. The lava lamp will + scale to fit any container size you specify! +

+
<div style="width: 200px; height: 350px;">
+  <script src="https://ritual.sh/js/adoptables/lavalamp.js"
+          data-bg-color-1="#F8E45C"
+          data-bg-color-2="#FF7800"
+          data-blob-color-1="#FF4500"
+          data-blob-color-2="#FF6347"
+          data-case-color="#333333"
+          data-blob-count="6"
+          data-speed="1.0"
+          data-blob-size="1.0"></script>
+</div>
+ +

Customization Options

+
    +
  • + data-bg-color-1 & data-bg-color-2: + Background gradient colors +
  • +
  • + data-blob-color-1 & data-blob-color-2: + Blob gradient colors +
  • +
  • + data-case-color: Color for the top cap and bottom base +
  • +
  • data-blob-count: Number of blobs (3-12)
  • +
  • + data-speed: Animation speed multiplier (0.5-1.5x) +
  • +
  • data-blob-size: Blob size multiplier (0.5-2.0x)
  • +
+ +

+ 🌟 Create your own custom lava lamp at + ritual.sh/resources/adoptables +

+
+
+ +