@sdetweil said in Contrasting text on changing background:
@mwm341 I want that too. text over photos.  haven’t figured out a way yet.
I kludged something together today with the MMM-Wallpaper module. It’s not elegant, but it’s been working for me so far.
What I’m doing is having the module draw the image onto an off-screen canvas and compute its average brightness using the luminance formula. Based on whether this brightness exceeds a defined threshold (115 has been working for me), the module then updates a global CSS variable with either a light or dark text color.
I’m using color: var(--dynamic-text-color); as the variable. Since it’s determining global brightness, it can still “miss” picking a good color for each module.
Depending on compute load, I’m thinking an update would be to determine that brightness value for each quadrant of an image then creating a variable for each quadrant and setting the text style in those areas to that color.  A downside to this approach is that it won’t work perfectly with different display aspect ratios, but if you’re only ever using a 16:9 display that would be mitigated.
Brightness function:
  // Helper function to compute average brightness of an image.
  getAverageBrightness: function(image, callback) {
    var canvas = document.createElement("canvas");
    var width = image.naturalWidth;
    var height = image.naturalHeight;
    canvas.width = width;
    canvas.height = height;
    var context = canvas.getContext("2d");
    context.drawImage(image, 0, 0, width, height);
    try {
      var imageData = context.getImageData(0, 0, width, height);
    } catch (error) {
      console.error("Error accessing image data:", error);
      callback(255);  // Assume bright background if error.
      return;
    }
    
    var data = imageData.data;
    var colorSum = 0;
    var pixels = data.length / 4;
    for (var i = 0; i < data.length; i += 4) {
      var r = data[i];
      var g = data[i + 1];
      var b = data[i + 2];
      // Calculate brightness using the luminance formula.
      var brightness = 0.299 * r + 0.587 * g + 0.114 * b;
      colorSum += brightness;
    }
    var averageBrightness = colorSum / pixels;
    callback(averageBrightness);
  },
Updated onImageLoaded function:
  onImageLoaded: function(imageData, element) {
    var self = this;
    return () => {
      self.resetLoadImageTimer();
      element.className = `wallpaper ${self.config.crossfade ? "crossfade-image" : ""}`;
      element.style.opacity = 1;
      
      // Analyze the image brightness and adjust text color accordingly.
      // This will update both the module's caption and a global CSS variable.
      self.getAverageBrightness(element, function(brightness) {
        var threshold = 128; // Adjust this threshold as needed.
        var textColor = brightness > threshold ? "black" : "white";
        self.title.style.color = textColor;
        // Set a global CSS variable for dynamic text color.
        document.documentElement.style.setProperty('--dynamic-text-color', textColor);
      });
      self.title.style.display = "none";
      setTimeout(() => {
        var caption = imageData.caption;
        if (self.config.caption && caption) {
          self.title.innerHTML = caption;
          self.title.style.display = "initial";
        }
        if (self.imageElement !== null) {
          self.content.removeChild(self.imageElement);
        }
        self.imageElement = self.nextImageElement;
        self.nextImageElement = null;
      }, self.config.crossfade ? 1000 : 0);
    };
  },