Creating a gradient mesh in CSS/Jquery

In my first answer, I interpreted this going more in the "artistic" way than in the "mathematical" way. The answer from Matt Coughlin make me think about a more mathematic answer, in wich we would make the "mesh" part of the requirement more rigorous.

That is, we have a matrix of colors, and we want to show this in a grid. If the grid has a spacing of say 100px, then the color [x][y] of the matrix would be given to the pixel in 100x and 100y. The pixels in between would be aproximated in a bilinear way.

For such an approach, the css would be:

.mesh { overflow: hidden; position: absolute;   width: 300px;   height: 300px;    }

.tile {    width: 200px;    height: 200px;    position: absolute;    display: block;   }

.tile11, .tile21, .tile31 {
left: -50px;
}
.tile12, .tile22, .tile32 {
left: 50px;
}
.tile13, .tile23, .tile33 {
left: 150px;
}
.tile11, .tile12, .tile13 {
top: -50px;
}
.tile21, .tile22, .tile23 {
    top: 50px;
}
.tile31, .tile32, .tile33 {
top: 150px;
}

.tile11 {
background: -webkit-radial-gradient(center center, 100px 100px, 
      rgba(255, 0, 0, 1) 0%, 
      rgba(255, 0, 0, 0.5) 50%,
      rgba(255, 0, 0, 0) 100%);}

.tile12 {
background: -webkit-radial-gradient(center center, 100px 100px, 
      rgba(255, 0, 0, 1) 0%, 
      rgba(255, 0, 0, 0.5) 50%,
      rgba(255, 0, 0, 0) 100%);}

I am using every div as a local contributor of the mesh, getting only to "touch" the inmediate neighbour, and going to a full transparency beyond that point.

The result is this :

fiddle

The 2 first colors are both red as a test. In a perfect system, the line connecting the 2 points should be perfect red all the time.

It's true that it's not a perfect result, but the "muddying" or "washyness" of the result is more or less avoided


At present

I experimented with something along these lines a few years ago, using SVG, the HTML5 canvas tag, and more recently CSS3 gradients. I don't believe there's a natural way to go beyond simple linear or radial gradients at present.

The question is if a mesh gradient can be simulated using only simple linear and radial gradients (which are the only tools available).

Unfortunately, when you combine multiple gradients using opacity or rgba, the colors of the different gradients tend to combine in a way that's not useful, leading to washed-out colors. Avoiding this would require being able to render it in the browser as a single complex gradient.

There are also significant limitations to the shapes the gradients can have -- linear gradients at an arbitrary angle, and elliptical gradients with radial symmetry. Neither allows for free-form, irregular shapes. The 2D transformations that can be applied to the resulting image are fairly regular in nature as well (scaling, skewing, etc).

In the future

The most promising option I'm aware of for the near future is the possible support for mesh gradients in SVG 2.0 (and perhaps diffusion curves as well). If this does happen and it's eventually supported by browsers, that should start to greatly expand the options. And the HTML5 canvas tag and CSS3 may follow soon afterwards.

And as @vals pointed out in the comment below, WebGL should offer some very promising options (for HTML5 canvas tags using a 3D context).

Related links

  • http://www.svgopen.org/2011/papers/18-Advanced_Gradients_for_SVG/
  • http://w3-org.9356.n7.nabble.com/Diffusion-Curves-td146288.html

I have done a simple layout to demostrate this.

First, I will put 4 divs, the first to show the partial results, and the last to see the final result. The markup is just:

<div class="box mesh1"></div>
<div class="box mesh2"></div>
<div class="box mesh3"></div> 
<div class="box mesh"></div> 

here box is just for dimensions, mesh1 to 3 hold the partial results, in mesh we put it all together.

The CSS is:

.box {
    width: 400px;
    height: 150px;
    position: relative;
    display: inline-block;
}

.mesh1, .mesh {
background:
    -webkit-linear-gradient(5deg,  rgba(0, 250, 0, 0.5), rgba(0, 100, 0, 0.5))
}

.mesh:after, .mesh:before {
    content: "";
    position: absolute;
    left: 0px;
    bottom: 0px;
    top: 0px;
    right: 0px;
}
.mesh2, .mesh:after {
background:   -webkit-radial-gradient(center center, circle cover, rgba(250, 0, 0, 0.6) 0%, rgba(120, 0, 10, 0.5) 100%);}

.mesh3, .mesh:before {
background: -webkit-radial-gradient(10% 10%, ellipse cover, rgba(0, 0, 250, 0.6) 0%, white 100%);}

I am giving to the mesh1 class a background linear, inclinated 5 degrees, and specifying colors in rgba format to allow for transparency.

Then, to be able to overlay more gradients, I specify to pseudo elements as before and after, sharing the same layout properties.

To the after element I give a circular gradient, shared with the mesh2 To the before element I give an elliptical gradient, off center. All of them can be rgba.

At the end, you see in the mesh div the result of overlapping everything

(I have used everywhere the webkit notation to make it short)

I wouldn't say it is much artistic, but I leave this part to you :-)

fiddle here


I wrote about a solution for creating lightweight, scalable mesh gradients using raster images: https://peterhrynkow.com/performance/2019/01/13/blowing-up-images-to-make-them-small.html

It doesn’t solve your problem of wanting to generate them randomly but at least gives you the small file size and resolution-independent scaling that you’d expect from a CSS or SVG solution.

Tags:

Css

Jquery