Saturday, September 27, 2025

Tutorial: Installing OpenProject on Raspberry Pi Using Docker


 OpenProject is an open-source project management tool that works well on a Raspberry Pi (RPi) via Docker, provided you're using a 64-bit OS (like Raspberry Pi OS 64-bit) and a model with at least 4GB RAM (e.g., Pi 4 or Pi 5). This tutorial assumes you already have Docker installed and running on your RPi. If not, you can install it quickly with:


curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

Log out and back in after the last command to apply group changes. We'll use the official all-in-one Docker container for a quick setup. This is ideal for testing or small-scale use, but for production, consider the Docker Compose method for better scalability.

Prerequisites

  • Raspberry Pi with 64-bit Raspberry Pi OS (Lite version recommended for efficiency).
  • At least 4GB RAM and a microSD card (16GB+ Class 10 or better).
  • Docker installed and running.
  • Access to your RPi's terminal (via SSH or directly).
  • Optional: An external drive (e.g., USB SSD) for persistent storage to avoid filling your SD card.

Step 0: Check the Latest OpenProject Version

OpenProject releases Docker images regularly. Check the latest stable version on Docker Hub:

docker search openproject/openproject

Or visit Docker Hub in a browser and look for the arm64 tag (e.g., 16 as of late 2024). Use the highest number available that's tagged for ARM64. 

Step 1: Verify Docker is Running

Ensure Docker is active:

sudo systemctl status docker

If not running, start it:

sudo systemctl start docker
Step 2: Create Directories for Persistent Storage

Create folders to store OpenProject’s database and assets (to persist data across container restarts). To prevent data loss on container restarts, create directories for the database and assets. Use an external drive if possible (e.g., mounted at /mnt/usb).

sudo mkdir -p /var/lib/openproject/{pgdata,assets}
sudo chown -R 1000:1000 /var/lib/openproject

If using an external drive (e.g., mounted at /mnt/usb):

sudo mkdir -p /mnt/usb/openproject/{pgdata,assets}
sudo chown -R 1000:1000 /mnt/usb/openproject

Step 3: Pull and Run the OpenProject Docker Container

Run the following command to pull and start the OpenProject container. Replace:

  • 192.168.1.100 with your Raspberry Pi’s IP address (find it with hostname -I).
  • secret with a strong key (generate with openssl rand -hex 32).
  • 16 with the latest version (check Docker Hub for arm64 tags; as of late 2024, 16 is recent).
 docker run -d \
  -p 8080:80 \
  --name openproject \
  -e OPENPROJECT_SECRET_KEY_BASE=secret \
  -e OPENPROJECT_HOST__NAME=192.168.1.100:8080 \
  -e OPENPROJECT_HTTPS=false \
  -e OPENPROJECT_DEFAULT__LANGUAGE=en \
  -v /var/lib/openproject/pgdata:/var/openproject/pgdata \
  -v /var/lib/openproject/assets:/var/openproject/assets \
  openproject/openproject:16
Explanation:
  • -p 8080:80: Maps port 8080 on your Pi to the container’s port 80.
  • -e: Sets environment variables (host, no HTTPS, English language).
  • -v: Links persistent storage.
  • -d: Runs in the background.

The image will download (may take a few minutes). Check progress:

docker logs -f openproject

Wait for messages like "Database configuration done" and "Memcached configuration done."

Step 4: Access OpenProject

  • Open a browser on any device on your network.
  • Navigate to http://<your-pi-ip>:8080 (e.g., http://192.168.1.100:8080).
  • Log in with:
    • Username: admin
    • Password: admin
  • Change the password immediately after logging in.

Step 5: Verify Installation

If the web interface loads and you can log in, the installation is successful. Configure settings (e.g., email) via Admin > Emails & Notifications.

Troubleshooting

  • Port Conflict: If 8080 is in use, change to another port (e.g., -p 8081:80) and update OPENPROJECT_HOST__NAME.
  • ARM64 Issues: Ensure your OS is 64-bit (uname -m should return aarch64). Use an -arm64 tagged image if available.
  • Slow Startup: Raspberry Pi’s limited resources may slow initialization. Monitor with docker stats.

If you encounter errors, share the output of docker logs openproject for further assistance. For advanced setups (e.g., separate database), consider Docker Compose as mentioned previously, but this single-container method is simplest for getting started.

This setup gives you a fully functional OpenProject instance for local project management. For more config options, check the official docs.



Updating Raspberry Pi OS Bookworm: Handling the initramfs.conf Prompt


My First Time Updating a Raspberry Pi with apt update

Updating a Raspberry Pi for the first time can feel like a rite of passage for anyone diving into the world of single-board computers. The Raspberry Pi, running a Linux-based operating system like Raspberry Pi OS, relies on tools like apt to keep its software fresh and secure. In this post, I’ll walk you through my experience performing my first apt update and apt upgrade, including a moment of decision when I encountered a configuration file prompt, what I learned by checking the differences, and how I wrapped it all up with a cleanup command.

Step 1: Opening the Terminal

I started by opening the terminal on my Raspberry Pi. If you’re using the graphical interface, you can find the terminal in the menu or press Ctrl + Alt + T. If you’re working over SSH or on a headless setup, you’re already in the right place.

Step 2: Running apt update

To begin, I ran the following command to update the package lists for upgrades and new package installations:

sudo apt update

The sudo part is necessary because updating the system requires superuser privileges. This command refreshes the list of available packages and their versions from the repositories. After running it, I saw lines showing the system fetching data, ending with a summary like “All packages are up to date” or a list of packages that could be upgraded.

Step 3: Running apt upgrade

Next, I wanted to install the available updates, so I ran:

sudo apt upgrade

This command downloads and installs the latest versions of the packages. Progress bars showed downloads and installations in action, but then I hit a prompt that made me pause.

Step 4: The Configuration File Prompt

During the upgrade, I encountered this message:

Configuration file '/etc/initramfs-tools/initramfs.conf'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a new shell to examine the situation
 The default action is to keep your current version.
*** initramfs.conf (Y/I/N/O/D/Z) [default=N] ?

This prompt indicated that the initramfs.conf file, part of the initramfs-tools package used to generate the initial ramdisk for booting, had been modified since installation. The package maintainer provided a new version, and I needed to decide what to do.

Understanding the Options

Here’s what I learned about the options:

  • Y or I: Install the package maintainer’s new version, overwriting my current file. This ensures the latest configuration but might discard custom changes.
  • N or O: Keep my current version, preserving modifications but potentially missing updates or fixes.
  • D: Show the differences between the two versions, useful for comparing changes.
  • Z: Open a shell to investigate further, ideal for advanced users.

Since this was my first update and I hadn’t intentionally modified initramfs.conf, I was curious about the changes. I first selected D to view the differences.

Step 5: Analyzing the Differences

Selecting D showed a diff between my current file (/etc/initramfs-tools/initramfs.conf) and the maintainer’s new version (/etc/initramfs-tools/initramfs.conf.dpkg-new). Here’s what I saw:

--- /etc/initramfs-tools/initramfs.conf 2025-05-12 20:17:48.163045799 -0400
+++ /etc/initramfs-tools/initramfs.conf.dpkg-new 2025-07-21 03:33:02.000000000 -0400
@@ -17,7 +17,7 @@
 # list - Only include modules from the 'additional modules' list
 #

-MODULES=dep
+MODULES=most

 #
 # BUSYBOX: [ y | n | auto ]
@@ -74,11 +74,11 @@
 #
 # RUNSIZE: ...
 #
-# The size of the /run tmpfs mount point, like 256M or 10%
+# The size of the /run tmpfs mount point, like 256M or 20%
 # Overridden by optional initramfs.runsize= bootarg
 #

-RUNSIZE=10%
+RUNSIZE=20%

Breaking Down the Changes

The diff revealed two changes:

  1. MODULES: Changed from dep to most.
    • dep means the initramfs includes only modules needed for the specific hardware, keeping the initramfs small.
    • most includes a broader set of modules, ensuring compatibility with a wider range of hardware but increasing the initramfs size.
  2. RUNSIZE: Changed from 10% to 20%.
    • This setting controls the size of the /run tmpfs mount point, used for temporary files during boot. Increasing it to 20% allocates more memory, potentially improving performance for systems needing more temporary storage during boot.

Since I hadn’t made these changes myself, they were likely made by a script or another package. The maintainer’s version seemed to prioritize broader compatibility (MODULES=most) and more memory allocation (RUNSIZE=20%), which made sense for a general-purpose update.

My Decision: Choosing “Yes”

After reviewing the diff, I chose Y to install the package maintainer’s version. I typed Y and pressed Enter. My reasoning was that I hadn’t customized initramfs.conf intentionally, and the changes (broader module support and increased tmpfs size) were likely beneficial for stability and compatibility with the updated system. If I had specific hardware constraints or custom boot settings, I might have chosen N to keep my version or merged changes manually after selecting Z, but Y was the safest choice for a beginner like me.

The upgrade continued, installing the new version of the configuration file and completing the package updates without further prompts.

Step 6: Cleaning Up with apt autoremove

Once the upgrade finished, I wanted to clean up unneeded packages to free up space. I ran:

sudo apt autoremove

This command removes packages that were automatically installed as dependencies but are no longer required. It freed up some disk space, which is especially helpful on a Raspberry Pi with limited storage.

Step 7: Verifying the Update

To ensure everything was applied correctly, I rebooted my Raspberry Pi with:

sudo reboot

After rebooting, I checked that my system was running smoothly. Everything worked as expected, confirming the update was successful.

Key Takeaways

  • Run sudo apt update first to refresh package lists, then sudo apt upgrade to install updates.
  • Configuration file prompts like the one for initramfs.conf are normal. Use D to inspect differences, which can help you decide between Y (maintainer’s version) or N (keep yours).
  • The diff for initramfs.conf showed changes to MODULES (from dep to most) and RUNSIZE (from 10% to 20%), which improve compatibility and allocate more memory for boot processes.
  • Choosing Y is often safe for beginners if you haven’t customized the file.
  • Clean up with sudo apt autoremove to remove unneeded packages.
  • Reboot after updates to ensure changes take effect.

Updating my Raspberry Pi was a learning experience, especially with the configuration file prompt. Checking the differences with D gave me confidence in choosing Y, and the cleanup command kept my system tidy. I hope this guide helps other first-timers navigate their Raspberry Pi updates with ease!

Monday, September 15, 2025

Blogger: Google Code Prettify

To set up Google Code Prettify on your Blogger blog for syntax highlighting, follow these steps:

Add the Prettify CSS and JavaScript

  • Go to your Blogger Dashboard.
  • Navigate to Theme > Edit HTML.
  • Find the <head> section of your template.

Insert the following lines just before the closing </head> tag:


<link href='https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/prettify.css' rel='stylesheet'/>
<script src='https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js'></script>

Use Code Blocks in Your Posts 

  • When writing a blog post, switch to the HTML view (click "HTML" in the post editor). 
  • Wrap your code using <pre> and <code> tags, and add the prettyprint class:
 

<pre class="prettyprint">
<code>
function helloWorld() {
  console.log("Hello, world!");
}
</code>
</pre>  

3. Optional: Specify Language for Better Parsing You can help Prettify detect the language by adding a language class:

<pre class="prettyprint lang-js">
<code>
// JavaScript code
var x = 42;
</code>
</pre> 

Common language classes:

  • lang-js – JavaScript 
  • lang-html – HTML/XML 
  • lang-css – CSS 
  • lang-python – Python 
  • lang-java – Java 

Handle Unescaped HTML in Code Blocks

If your code contains unescaped HTML characters (e.g., <, >, &), Prettify may not render them correctly. To fix this, add the following script just before the closing tag to automatically escape these characters within tags, while avoiding double-escaping already escaped entities:

WOKING ON IT << the Challenge

This script runs after the page loads, finds all <code> elements within <pre class="prettyprint">, and converts unescaped HTML characters to their proper entities before Prettify processes them. .

Preview and Publish 

  • Preview your post to ensure code is highlighted correctly.
  •  Publish when satisfied.

 ✅ Tips:

  • Prettify automatically highlights all elements with class="prettyprint" on page load—no extra JavaScript needed. 
  • Escape your code. <. > and & all cause challenges with blogger, so showing code is a challenge, creating a script to do that for us is even more of a challenge. 
  • For dark themes, you can replace the CSS link with a themed version (e.g., sons-of-obsidian.css):

<link href='https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/skins/sons-of-obsidian.css' rel='stylesheet'/> 

Now your code snippets will be cleanly formatted and syntax-highlighted using Google Code Prettify.

Blogger: Scroll‑To‑Top Button

Below is a complete, copy‑and‑paste ready snippet that you can drop into any Blogger (blogspot) XML template.


It creates a small “scroll‑to‑top” button that sits in the lower‑right corner of the screen and fades in/out as you scroll.

How to add it


Open your Blogger dashboard → Theme → Edit HTML.
In the template editor, find the closing </body> tag (or any place before it).
Paste the entire block that starts with <style> and ends with </script> right before </body>.
Save your theme.
The code uses only vanilla JavaScript, so no external libraries are required.

<!-- ======== Scroll-to-Top Button ======== -->
<style>
  /* The button itself */
  #scrollToTopBtn {
    position: fixed;
    bottom: 30px;          /* distance from the bottom of viewport */
    right: 30px;           /* distance from the right side of viewport */
    z-index: 9999;         /* stay on top of everything else */
    width: 48px;
    height: 48px;
    border-radius: 50%;    /* make it circular */
    background-color: #555;/* default colour – change as needed */
    color: white;
    font-size: 24px;
    line-height: 48px;     /* vertical centering of the icon */
    text-align: center;
    cursor: pointer;
    opacity: 0;            /* start hidden */
    transition: opacity .4s ease-in-out, background-color .3s;
    box-shadow: 0 2px 6px rgba(0,0,0,.3);
  }
  #scrollToTopBtn:hover {
    background-color: #333;/* darker on hover */
  }
  /* Show the button when it has class 'show' */
  #scrollToTopBtn.show { opacity: 1; }
</style>

<div id="scrollToTopBtn" title="Back to top">&#8679;</div> <!-- ▲ or &#x21E7; -->

<script>
(function(){
  var btn = document.getElementById('scrollToTopBtn');

  // Show/hide button based on scroll position
  window.addEventListener('scroll', function() {
    if (window.pageYOffset > 300) {   // show after scrolling down 300px
      btn.classList.add('show');
    } else {
      btn.classList.remove('show');
    }
  });

  // Smooth scroll to top when button is clicked
  btn.addEventListener('click', function() {
    window.scrollTo({top:0, behavior:'smooth'});
  });
})();
</script>
<!-- ======== End of Scroll-to-Top Button ======== -->

What each part does Section


Section Purpose
CSS (#scrollToTopBtn) Styles the button: fixed position, circle, colors, transition effects.
HTML (<div>) The actual button element. Uses a Unicode arrow (&#8679; ▲). You can swap it for an image or Font Awesome icon if you prefer.
JavaScript 1. Listens to scroll events – shows the button after scrolling past 300 px, hides it otherwise.
2. On click, smoothly scrolls back to the top of the page.

Customisation tips

  • Button colour: change background-color in the CSS block.
  • Size/position: modify bottom, right, width, height.
  • Scroll threshold: adjust the number 300 in the script if you want it to appear earlier/later.
  • Icon: replace the arrow with an image or Font Awesome icon by editing the innerHTML of #scrollToTopBtn.
That’s all! Your Blogger site will now have a tidy, responsive scroll‑to‑top button that works on desktop and mobile.

Blogger: Copy Code Button for Google Code Prettify

How to Add a Copy Code Button to Google Code Prettify in Blogger

If you use Google Code Prettify to display code snippets on your Blogger blog, you might want to make it easier for your readers to copy the code with a single click. Adding a "Copy" button to each code block enhances user experience and makes your blog more professional. In this post, I'll guide you through the process of adding a copy button to your Google Code Prettify code blocks in a Blogger template. The button will appear in the top-right corner of each code block, and when clicked, it copies the code to the clipboard with a "Copied!" confirmation.

Prerequisites

  • Your Blogger template already uses Google Code Prettify (you've included the Prettify script and CSS in your template).
  • Basic knowledge of editing Blogger templates (don't worry, I'll walk you through each step!).
  • Access to your Blogger dashboard to modify the template.

Step-by-Step Guide

Step 1: Back Up Your Template

Before making changes to your Blogger template, always create a backup to avoid losing your work.

  1. Go to your Blogger Dashboard.
  2. Navigate to Template > Edit HTML.
  3. Click the Download Template button to save a copy of your current template.

Step 2: Add CSS for the Copy Button

We'll add CSS to style the code blocks and the copy button, ensuring a consistent look with rounded corners and a white background across all pages (homepage, archive, and posts).

  1. In the Blogger template editor (Template > Edit HTML), locate the <b:skin> section. It starts with <b:skin><![CDATA[ and ends with ]]></b:skin>.
  2. Just before the closing ]]></b:skin>, add the following CSS code:
/* Code Block Copy Button
----------------------------------------------- */
pre.prettyprint {
  position: relative;
  padding: 30px 10px 10px 10px; /* Top padding for button, consistent padding for content */
  background-color: #ffffff; /* White background */
  border: 1px solid #cccccc; /* Light gray border */
  border-radius: 5px; /* Rounded corners */
  overflow: auto; /* Handle long code lines */
  font-family: &apos;Courier New&apos;, Courier, monospace; /* Consistent code font */
  font-size: 14px; /* Readable font size */
}

.copy-button {
  position: absolute;
  top: 5px;
  right: 5px;
  background-color: $(link.color); /* Matches your theme&apos;s link color */
  color: $(mobile.button.color); /* Matches your theme&apos;s button text color */
  border: none;
  padding: 5px 10px;
  cursor: pointer;
  font-size: 12px;
  border-radius: 3px;
}

.copy-button:hover {
  background-color: $(link.hover.color); /* Matches your theme&apos;s hover color */
}

.copy-button.copied::after {
  content: &apos;Copied!&apos;;
  position: absolute;
  top: 5px;
  right: 30px;
  background-color: #333;
  color: #fff;
  padding: 2px 5px;
  border-radius: 3px;
  font-size: 10px;
}

This CSS:

  • Styles code blocks with a white background, rounded corners, and a border.
  • Positions the copy button in the top-right corner.
  • Uses Blogger theme variables ($(link.color), $(mobile.button.color), $(link.hover.color)) to match your blog's color scheme.
  • Adds a "Copied!" popup when the code is copied.

Step 3: Add JavaScript for Copy Functionality

Next, we'll add JavaScript to dynamically insert the copy button into each code block and handle the copying process using the Clipboard API.

  1. In the template editor, find the closing </head> tag.
  2. Just before </head>, add the following JavaScript code:
<script type='text/javascript'>
//<![CDATA[
document.addEventListener('DOMContentLoaded', function() {
  // Find all <pre> elements with class 'prettyprint'
  const codeBlocks = document.querySelectorAll('pre.prettyprint');
  
  codeBlocks.forEach(function(pre) {
    // Create copy button
    const button = document.createElement('button');
    button.className = 'copy-button';
    button.textContent = 'Copy';
    
    // Add click event listener for copying
    button.addEventListener('click', function() {
      // Get the text content of the <code> element inside <pre>, or fallback to pre's text excluding the button
      let code = '';
      const codeElement = pre.querySelector('code');
      if (codeElement) {
        code = codeElement.textContent;
      } else {
        // Clone the pre element to avoid modifying the original
        const preClone = pre.cloneNode(true);
        // Remove the copy button from the clone
        const buttonInClone = preClone.querySelector('.copy-button');
        if (buttonInClone) {
          buttonInClone.remove();
        }
        code = preClone.textContent;
      }
      
      // Use Clipboard API to copy text
      navigator.clipboard.writeText(code).then(function() {
        // Show 'Copied!' feedback
        button.classList.add('copied');
        setTimeout(function() {
          button.classList.remove('copied');
        }, 2000); // Remove 'Copied!' after 2 seconds
      }).catch(function(err) {
        console.error('Failed to copy: ', err);
      });
    });
    
    // Append button to the <pre> element
    pre.appendChild(button);
  });
});
//]]>
</script>

This JavaScript:

  • Waits for the page to load (DOMContentLoaded).
  • Finds all <pre class="prettyprint"> elements.
  • Adds a "Copy" button to each code block.
  • Copies only the code content (excluding the button's text) when clicked.
  • Shows a "Copied!" confirmation for 2 seconds.

Step 4: Save and Test

  1. Click Save in the Blogger template editor.
  2. Preview your blog to ensure the copy button appears in the top-right corner of each code block.
  3. Test the copy functionality by clicking the button on a code block. The code should copy to your clipboard without including the "Copy" text, and you should see a "Copied!" popup.
  4. Check the homepage, archive, and post pages to confirm consistent styling (white background, rounded corners).

Troubleshooting Tips

  • Button Not Appearing: Ensure the Google Code Prettify script and CSS are included in your template's <head>:
<link href='https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/prettify.css' rel='stylesheet'/>
<script src='https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js'/>
  • Inconsistent Styling: If code blocks look different on archive or post pages, double-check that the CSS is within the <b:skin> section and not overridden by other styles. You can add !important to critical styles (e.g., background-color: #ffffff !important) if needed.
  • Copy Includes "Copy" Text: If the copied text includes the word "Copy," ensure you're using the exact JavaScript code above, which excludes the button's text.
  • Button Styling Issues: If the button's colors don't match your theme, replace $(link.color) and $(mobile.button.color) with specific colors (e.g., #dd7700 for the button background, #ffffff for text).

Example

Here's how a code block will look in your posts (assuming you use <pre class="prettyprint"> for your code):

<pre class="prettyprint lang-html">
function helloWorld() {
  console.log("Hello, World!");
}
</pre>

This will display with a white background, rounded corners, and a "Copy" button in the top-right corner. Clicking the button copies the code (function helloWorld() { console.log("Hello, World!"); }) to the clipboard.

Conclusion

Adding a copy button to your Google Code Prettify code blocks is a simple way to improve your blog's usability. With just a few lines of CSS and JavaScript, you can make your code snippets more accessible and give your blog a polished, professional look. Try it out, and let me know in the comments if you run into any issues or have questions!

Happy blogging! 🚀

Tuesday, September 9, 2025

The Impact of AI on Envato’s Marketplaces: Sales Trends and Submission Surge

The rise of AI coding tools since 2022 has reshaped digital marketplaces like Envato, which operates CodeCanyon (code scripts and plugins), ThemeForest (website themes and templates), and Envato Elements (a subscription-based library of digital assets). With AI tools like GitHub Copilot and ChatGPT enabling faster coding and design, two questions arise: Are sales on these platforms rising or falling? And are more coders submitting projects due to AI assistance? This post explores these trends based on available data and industry insights up to September 2025, noting that the Envato Forums, a key source of author feedback, closed on August 15, 2025, and will remain in read-only mode until the end of September 2025 [1]. Additionally, Envato’s blog appears to have been restructured or removed, limiting access to some historical data.

Sales Trends: A Mixed Picture

Overall Revenue Growth

Envato’s acquisition by Shutterstock in July 2024 has driven positive financial results. Shutterstock’s Q2 2025 earnings report (April–June 2025) shows a 21% year-over-year revenue increase to $267 million, with content revenue (including Envato’s marketplaces) up 18% to $199.8 million [2]. Envato’s integration and a growing subscriber base have bolstered this growth, suggesting aggregate sales across CodeCanyon, ThemeForest, and Envato Elements are trending upward. Net income surged to $29.4 million from $3.6 million in Q2 2024, highlighting the acquisition’s impact.

However, this high-level growth contrasts with challenges for individual authors, as AI tools reshape buyer behavior and market dynamics.

Marketplace-Specific Trends

CodeCanyon: Declining Individual Sales

CodeCanyon has faced significant sales declines for individual authors. Prior to the Envato Forums’ closure on August 15, 2025, author discussions indicated a 70% drop in overall marketplace sales from 2018 to 2023, with CodeCanyon’s monthly sales volume falling 63% (from 1 million in November 2022 to 372,000 in October 2023) [1] (read-only until end of September 2025). Authors reported personal revenue drops of up to 75% from 2019 peaks, with some noting “100% drops” for specific items by mid-2025 (based on archived community discussions, no longer fully accessible). AI’s impact is evident: coding tools allow buyers to create custom scripts, reducing demand for pre-built plugins.

Envato embraced AI by introducing an “AI Tools” category in 2023, increasing AI-related items (e.g., ChatGPT WordPress plugins) by 40% (based on industry reports and community discussions, as Envato’s blog is no longer accessible). While this drives engagement, it saturates the market, making it harder for traditional plugins to compete.

ThemeForest: Challenges Persist

ThemeForest mirrors CodeCanyon’s struggles. The 70% sales decline from 2018–2023 affected themes significantly, as AI tools like Midjourney enable custom designs, reducing reliance on pre-made templates [1] (read-only until end of September 2025). Authors reported ongoing issues in 2025, worsened by Envato’s focus on subscription-based Elements, which has impacted one-off theme sales (based on archived community discussions, no longer fully accessible). Shutterstock’s acquisition may stabilize ThemeForest by integrating themes into broader creative workflows, but individual author earnings remain pressured.

Envato Elements: Subscription Growth Amid Saturation

Envato Elements shows a complex scenario. Shutterstock’s 2025 reports highlight subscriber growth, driven by Elements’ unlimited-download model [2]. However, individual authors reported “steady declines” in per-item earnings, with some seeing 70% drops since 2018 (based on archived community discussions, no longer fully accessible). The platform’s asset volume has doubled or tripled in categories since 2020, diluting visibility for quality work. A “huge influx of new contributors” and AI-generated assets further saturates the library, disrupting traditional sales. AI-related searches and items (up 40% in 2024–2025) boost overall platform engagement (based on industry trends, as Envato’s blog is no longer accessible).

In summary, while aggregate sales are rising due to subscriptions and the Shutterstock acquisition, individual item sales on CodeCanyon, ThemeForest, and Elements are declining, largely due to AI-driven market saturation and reduced demand for pre-built assets.

Surge in Submissions: AI’s Role in Empowering Coders

AI has lowered the barrier to entry for coders, leading to a significant increase in project submissions across Envato’s platforms.

Evidence of Increased Submissions

  • Asset Growth: Envato Elements adds approximately 1 million files monthly in 2025, with code and theme categories doubling or tripling since 2020 (based on industry trends, as Envato’s blog is no longer accessible). CodeCanyon’s AI-related submissions (e.g., AI plugins) rose 40% by late 2023, a trend continuing into 2025 (based on archived community discussions).
  • Author Influx: Before the forums closed, authors noted a “huge influx of new contributors,” many leveraging AI to produce projects faster [1] (read-only until end of September 2025). While exact author numbers post-2018 (~1,500 full-time earners then) are unavailable, the asset surge suggests growth.
  • AI Coding Trends: Globally, 82% of developers used AI tools weekly in Q1 2025, up from 76% in 2024, boosting productivity and encouraging more submissions (based on industry reports). Envato’s 2023–2025 AI policies allow ethical AI use with disclosure, further incentivizing uploads [3].

Challenges of Increased Submissions

The submission surge has drawbacks. Increased competition lowers per-project earnings, and some AI-generated submissions are rejected for lacking human effort [3]. Less strict curation due to high volume also frustrates veteran authors, who struggle to stand out (based on archived community discussions, no longer fully accessible).

Conclusion

AI has transformed Envato’s marketplaces in complex ways. Aggregate sales are rising, driven by subscriptions and Shutterstock’s acquisition, but individual authors face declining earnings due to AI-driven market saturation and reduced demand for pre-built assets. Meanwhile, AI empowers more coders to submit projects, flooding platforms with content but intensifying competition. For creators, adapting to AI tools and focusing on unique, high-value offerings may be key to thriving in this evolving landscape.

The closure of the Envato Forums on August 15, 2025, has sparked significant community reaction, with authors expressing sadness, frustration, and nostalgia for the platform that fostered peer-to-peer support and transparency [1]. Comments highlight concerns about losing a vital space for discussing Envato’s decisions and sharing knowledge, with some authors feeling isolated as the company shifts to direct support channels and external platforms like Facebook groups. This closure, combined with the apparent removal of Envato’s blog, underscores the challenges authors face in maintaining community and accessing historical data amidst market pressures and AI-driven changes.

References

  1. Envato Forums: Important Update - Envato Forums Have Now Closed (read-only until end of September 2025)
  2. Shutterstock Q2 2025 Earnings Report
  3. Envato: AI-Generated Content Policy for Market and Elements

Building BlueMountainDJ.com: A Modern Wedding DJ Website with Tailwind CSS and JavaScript

Building BlueMountainDJ.com: A Modern Wedding DJ Website with Tailwind CSS and JavaScript As a web developer, I recently had the opportunit...