g | x | w | all
Bytes Lang Time Link
058Scratch 3.0 + Pen extension241225T183249Zmadeforl
nan241125T211618ZMark Jer
nan241017T165813Zccprog
nan241015T214758ZGeneral
nan241016T202552ZJordan
738Python241016T112517Zalephalp
nan241011T172746Zayreguit
nan241009T223609ZGreg Mar
nan241010T140202Zmadeforl
nan241010T015259Zsouth
nan241010T004245ZNeil
nan241009T124041ZLuis Men

Scratch 3.0 + Pen extension, 58 blocks

Picture of the code

check it out in action here

based off of Neil's answer

Floater (385 pixels)

Mexican hat contest entry (1:1 scale)

Enlarged:

Mexican hat contest entry (enlarged)

Image after execution:

enter image description here

Higher fidelity shot by changing the first pushed value from 6 to 12:

enter image description here

Bonus work-in-progress shot:

enter image description here

[Edit] Actually this can be smaller, but right now I can't be bothered. I spotted an unused value initialized on memory location 4.

HTML+CSS

The animation, depending on device performance, might give a few artifacts and obscure a bit which ring is drawn in front of which one. (which actually is fine, since otherwise it might be more obvious this solution is not really perfect.)

Edit: Tweaked the nature of the animation and the order of the rings in such a way that they are now mostly correctly aligned.

@property --r {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

body {
  display: flex;
  justify-content: center;
}
.base {
  width: 400px;
  animation: grow 1s linear infinite;
}
.ring {
  position: absolute;
  width: 400px;
  height: 400px;
  transform-style: preserve-3d;
  transform: rotateX(60deg) rotateZ(-20deg);
}
.ring::before {
  content: "";
  position: absolute;
  width: calc((var(--s) + var(--r)) * 100px);
  height: calc((var(--s) + var(--r)) * 100px);
  top: calc(200px - (var(--s) + var(--r)) * 50px);
  left: calc(200px - (var(--s) + var(--r)) * 50px);
  border: 6px solid hsl(calc((var(--s) + var(--r)) * 1rad) 100% 50%);
  border-radius: 50%;
  clip-path: inset(calc((var(--s) + var(--r)) * 50px - 200px));
  transform:  translateZ(calc(sin((var(--s) + var(--r)) * 1rad) * 100px))
}

@keyframes grow {
  from { --r: 0 }
  to { --r: 0.2 }
}
<div class="base">
  <div class="ring" style="--s: 4.8"></div>
  <div class="ring" style="--s: 4.6"></div>
  <div class="ring" style="--s: 5"></div>
  <div class="ring" style="--s: 4.4"></div>
  <div class="ring" style="--s: 5.2"></div>
  <div class="ring" style="--s: 4.2"></div>
  <div class="ring" style="--s: 5.4"></div>
  <div class="ring" style="--s: 4"></div>
  <div class="ring" style="--s: 5.6"></div>
  <div class="ring" style="--s: 3.8"></div>
  <div class="ring" style="--s: 3.6"></div>
  <div class="ring" style="--s: 3.4"></div>
  <div class="ring" style="--s: 3.2"></div>
  <div class="ring" style="--s: 0"></div>
  <div class="ring" style="--s: 3"></div>
  <div class="ring" style="--s: 0.2"></div>
  <div class="ring" style="--s: 2.8"></div>
  <div class="ring" style="--s: 0.4"></div>
  <div class="ring" style="--s: 2.6"></div>
  <div class="ring" style="--s: 0.6"></div>
  <div class="ring" style="--s: 2.4"></div>
  <div class="ring" style="--s: 0.8"></div>
  <div class="ring" style="--s: 2.2"></div>
  <div class="ring" style="--s: 1"></div>
  <div class="ring" style="--s: 2"></div>
  <div class="ring" style="--s: 1.2"></div>
  <div class="ring" style="--s: 1.8"></div>
  <div class="ring" style="--s: 1.4"></div>
  <div class="ring" style="--s: 1.6"></div>
</div>

If you want the HTML part a bit more compact, preprocessing with Pug will help; see it live.

Microsoft Excel

This is not at all optimized for character count, but that's not really a criterion for this one anyway.

Speed Optimizations:

=LET(
 tableWidth,80,
 resX,0.0625,resY,0.0625,
 width,8,
 dimX,width/resX,
 dimY,width/resY, thX,RADIANS(30),
 thZ,RADIANS(60),
 scale,8,
 sX,SIN(thX),cX,COS(thX),
 sZ,SIN(thZ),cZ,COS(thZ),
 rotMatrix,VSTACK(
  HSTACK(cX,-sX*cZ,sX*sZ),
  HSTACK(sX,cX*cZ,-cX*sZ),
  HSTACK(0,sZ,cZ)
 ),
 z,LAMBDA(i,j,SIN(SQRT(i*i+j*j))),
 plotAll,LAMBDA(pts,
  LET(
   zBufs,MAP(pts,LAMBDA(pt,0+INDEX(TEXTSPLIT(pt,"|"),1))),
   coords,MAP(pts,LAMBDA(pt,0+INDEX(TEXTSPLIT(pt,"|"),2))),
   vals,MAP(pts,LAMBDA(pt,0+INDEX(TEXTSPLIT(pt,"|"),3))),
   sorted,SORT(SORT(HSTACK(zBufs,coords,vals),1,1),2,1),
   indexCol,CHOOSECOLS(sorted,2),
   valueCol,CHOOSECOLS(sorted,3),
   MAKEARRAY(tableWidth,tableWidth,LAMBDA(row,col,
    LET(
     id,1000*col+row,
     check,MATCH(id,indexCol,1),
     IF(IFNA(INDEX(indexCol,check),2)=id,INDEX(valueCol,check),"")
    )
   ))
  )
 ),
 plotAll(
  TOCOL(MAKEARRAY(dimX,dimY,LAMBDA(row,col,
   LET(
    i,(col-1)*resX-width/2,
    j,(row-1)*resY-width/2,
    k,z(i,j),
    newPt,MMULT(rotMatrix,VSTACK(i*scale,j*scale,k*scale)),
    INDEX(newPt,3)&"|"
     &(1000*ROUND(40+INDEX(newPt,1),0)
      +ROUND(30+INDEX(newPt,2),0))&"|"
     &k
   )
  )))
 )
)

Screenshot for amusement: enter image description here

I added a Color Scale formatter for ease of viewing.

Google Sheets port

=LET(
 tableWidth,80,
 resX,0.0625,resY,0.0625,
 width,8,
 dimX,width/resX,
 dimY,width/resY,
 thX,RADIANS(30),
 thZ,RADIANS(60),
 scale,8,
 sX,SIN(thX),cX,COS(thX),
 sZ,SIN(thZ),cZ,COS(thZ),
 rotMatrix,{
  {cX,-sX*cZ,sX*sZ};
  {sX,cX*cZ,-cX*sZ};
  {0,sZ,cZ}
 },
 z,LAMBDA(i,j,SIN(SQRT(i*i+j*j))),
 plotAll,LAMBDA(pts,
  LET(
   sorted,SORT(pts,1,1,2,1),
   indexCol,CHOOSECOLS(sorted,1),
   valueCol,CHOOSECOLS(sorted,3),
   MAKEARRAY(tableWidth,tableWidth,LAMBDA(row,col,
    LET(
     id,1000*col+row,
     check,MATCH(id,indexCol,1),
     IF(IFNA(INDEX(indexCol,check),2)=id,INDEX(valueCol,check),"")
    )
   ))
  )
 ),
 plotAll(
  MAKEARRAY(dimX*dimY,1,LAMBDA(ptNo,_,
   LET(
    i,MOD(ptNo,dimX)*resX-width/2,
    j,INT(ptNo/dimX)*resY-width/2,
    k,z(i,j),
    newPt,MMULT(rotMatrix,VSTACK(i*scale,j*scale,k*scale)),
    {1000*ROUND(40+INDEX(newPt,1),0)
      +ROUND(30+INDEX(newPt,2),0),INDEX(newPt,3),k}
   )
  ))
 )
)

Port notes:

JavaScript

This was generated almost entirely by v0, an LLM programming assistant. I'm marking it as community wiki since there's currently no consensus on AI-generated answers. Maybe this will get some discussion moving.

After clicking on "Run code snippet" below you might have to scroll the iframe to see the hat. Click on "Full page" to see a bigger view of it.

const ctx = canvas.getContext('2d');

const width = canvas.width;
const height = canvas.height;

function drawPlot(rotationX, rotationY) {
  ctx.clearRect(0, 0, width, height);

  const scale = Math.min(width, height) / 8;
  const centerX = width / 2;
  const centerY = height / 2;

  // Create a depth buffer to handle overlapping points
  const depthBuffer = new Array(width * height).fill(Infinity);

  for (let x = -4; x <= 4; x += 0.1) {
    for (let y = -4; y <= 4; y += 0.1) {
      const distance = Math.sqrt(x * x + y * y);
      const z = Math.sin(distance);

      // 3D rotation
      const rotatedX = x * Math.cos(rotationY) + y * Math.sin(rotationY);
      const rotatedY = -x * Math.sin(rotationY) * Math.sin(rotationX) + y * Math.cos(rotationY) * Math.sin(rotationX) + z * Math.cos(rotationX);
      const rotatedZ = x * Math.sin(rotationY) * Math.cos(rotationX) - y * Math.cos(rotationY) * Math.cos(rotationX) + z * Math.sin(rotationX);

      // Project 3D to 2D
      const screenX = Math.round(centerX + rotatedX * scale);
      const screenY = Math.round(centerY - rotatedZ * scale);

      // Check if this point is closer than the previous point at this pixel
      const bufferIndex = screenY * width + screenX;
      if (screenX >= 0 && screenX < width && screenY >= 0 && screenY < height && rotatedY < depthBuffer[bufferIndex]) {
        depthBuffer[bufferIndex] = rotatedY;

        // Map z value to color
        const r = Math.floor(255 * (0.5 + 0.5 * Math.sin(Math.PI * z)));
        const g = Math.floor(255 * (0.5 + 0.5 * Math.cos(Math.PI * z / 2)));
        const b = Math.floor(255 * (0.5 + 0.5 * Math.sin(Math.PI * z / 4)));

        // Apply depth shading
        const depthFactor = 0.5 + (rotatedY + 4) / 8; // Normalize to 0.5-1 range
        ctx.fillStyle = `rgb(${r * depthFactor},${g * depthFactor},${b * depthFactor})`;
        ctx.fillRect(screenX, screenY, 2, 2);
      }
    }
  }
}

let rotationY = 0;

function animate() {
  drawPlot(Math.PI / 2.5, rotationY);
  rotationY += 0.01;
  requestAnimationFrame(animate);
}

animate();
body{background-color: #1a202c;}
<canvas id=canvas width=800 height=600></canvas>

Python, 738 bytes

import math

data = [[' '] * 100 for _ in range(30)]

for i in range(81):
    for j in range(81):
        x = -4 + i * 0.1
        y = -4 + j * 0.1
        r = math.sqrt(x**2 + y**2)
        z = math.sin(r)

        # normal vector
        length = math.sqrt(1 + math.cos(r)**2)
        nx = -x * math.cos(r) / r / length if r else 0
        ny = -y * math.cos(r) / r / length if r else 0
        nz = 1 / length

        # light intensity
        light = (ny + nz) / math.sqrt(2)
        char = " .-:=+*#%@"[int(light * 10)]

        # project to screen
        screen_x = 50 - round(x * 6 - y * 6)
        screen_y = 12 - round(z * 5 - x * 1.5 - y * 1.5)
        data[screen_y][screen_x] = char

for row in data:
    print(''.join(row))

Attempt This Online!

ASCII art with simple lighting.

                                                                                                    
                                                                                                    
                                                                                                    
                                            ==++*******++                                           
                                      :+**###%%%%%###########**                                     
                                  -:+**##%%@%@@@%%%%%####****#####*                                 
                               .-:=+*##%%@@@@@@@@@%%##***++++**######*                              
                              .::=+**###%%%@@@@@@@%%#**+=====+**##%%%%#                             
                            .--:==++**#####%@@@@@@%##*=::::=+**#%%%%%%%%%*                          
                           .--::==+++***###**+@@@@%#++-:=+**##%%@@@@@@@%%%##                        
                         ..--::===+++****#########*######%%%%%@@@@@@@@@@@%%%#                       
                     .  ..--:::===+++***#######%%%%%%%%%%@@@@@@@@@@@@@@@@@%%%###                    
                  :......---:::===+++****#####%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@%%%%###                 
               =:--....----:::===++++****#####%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@%%%%####              
            *+=:----.-----::::====+++****######%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%####           
  %%%%%%%##**==:--------::::=====++++****######%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%###*+==::-- 
   %%%%%%#**+==::::---:::::====+++++*****######%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%##*+==:--  
    %%%%###*++==::::::::::=====+++++*****######%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%#**++:::   
      %%###**+++==:::::=======++++++****#######%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%###*+==     
         ###**+++==========+++++++******#######%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%##**        
            ***++++++++=+++++++++*******#######%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%#           
                   +++++++++++*********#######%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@                  
                         ++**********#########%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@                        
                             ****###########%%%%%%%%%%%@@@@@@@@@@@@@@@@@                            
                                ###########%%%%%%%%%%%%@@@@@@@@@@@@@@                               
                                  #########%%%%%%%%%%%%%%%%%%%%%%@@                                 
                                     ###########%%%%%%%%%%####%%                                    
                                        ##*++==::----::=++*##                                       
                                                                                                    
                                                                                                    

Thumby MicroPython

import thumby
import math
while 1:
    m=[-100]*72
    thumby.display.fill(0)
    ry=(y/4 for y in range(-16, 17))
    for y in ry:
        a=2*y
        rx=(x/4 for x in range(-16, 17))
        for x in rx:
            z=12*math.sin(math.sqrt(x*x+y*y))
            x1=int(x*4+a)
            y1=int(z+a)
            if y1>m[x1]:
                m[x1]=y1
                thumby.display.setPixel(x1+36,18-y1,1)
    thumby.display.update()

Thumby is a tiny device the size of your thumb, programmed in Micropython with 72x40 pixel black & white screen. Try the above code by copy/pasting into the emulator here and clicking RESTART (assuming your browser can cope - works with most Chrome based browsers)

Code running on Thumby emulator

Wolfram Language (Mathematica)

Plot3D[Sin@Norm@{,y},{,-4,4},{y,-4,4}]

Mostly the obvious implementation, although Norm is a short way to access the square root of the sum of squares, and I did remember to save 2 bytes by letting one of the variables be the invisible Nulls before the commas (not that this is code-golf, but habits die hard) :p

Mexican hat picture

YASEPL

this is a translation of Neil's answer in ZX Spectrum BASIC. really cool dude!

=i£1`1!1©0!+}2,255$-5`2=j-4`3!o$j^!z$i^+o&≈0,z!o$j*4!x$i*16+128-o!o$j*8!l$z*32!y$i*2+96+o+l!q$x(!r$y(!t¥q,1}5,y,4!1¤x,y!p$27›$91›!r/3(!n$50-r~!p$59›!q/2(~#"H">"o"`4!h$/4!j+h}2,4,3!h$/16!i+h}2,4,2

how it works:

=i£1`1!1©0!+}2,255                           - create list1 with 255 0s
$-5                                          - set I to -4
`2                                           - do {
=j-4                                         -     set J to -4
`3                                           -     do {
!o$j^!z$i^+o&≈0,z                            -         set Z to sin(sqrt(I^2+J^2))
!o$j*4!x$i*16+128-o                          -         set X to (I*16)+128-(J*4)
!o$j*8!l$z*32!y$i*2+96+o+l                   -         set Y to (I*2)+96+(J*8)+(Z*32)
!q$x(!r$y(                                   -         set Q & R to integer versions of X and Y
!t¥q,1}5,y,4                                 -         if list1(Q) < Y {
!1¤x,y                                       -             set list1(X) to Y
!p$27›$91›!r/3(!n$50-r~!p$59›!q/2(~#"H">"o"  -             plot Q and R on places on the screen
`4                                           -         }
!h$/4!j+h                                    -         J += 0.25
}2,4,3                                       -     } while J < 4
!h$/16!i+h                                   -     I += 0.0625
}2,4,2                                       - } while I > 4

a picture demonstrating the mexican hat challenge done in yasepl

J plot

plot _4 4([;;)'1 o.|@j.'

For more information on how function plots work, see wiki/Plot/Function. For the golfers, this is 24 bytes.

plot _4 4([;;)'1 o.|@j.'
              '1 o.|@j.'   NB. string repr of a dyadic fork used by plot
                   |@j.    NB. magnitude of the complex number xjy
               1 o.        NB. sin
     _4 4                  NB. plot range
         ([;;)             NB. dyadic fork
            ;              NB. build a boxed list of _4 4 and the function string
          [                NB. _4 4
           ;               NB. box again
plot                       NB. plot

A Mexican hat shown in a jQt plot window

ZX Spectrum Basic

Very Slow Program

source code

Output

output

Less Slow Program

source code

Output

output

Sorry for source screenshots but I was too lazy to retype everything.

MATL

KUQ_ZvqK/t!YyY,lYH

Try it at MATL Online!

Some obscure features:

How it works

KUQ   % Push 4, square, add 1: gives 17
_Zv   % Symmetric range: gives row vector [17 16 ... 2 1 2 ... 16 17]
q     % Subtract 1, element-wise: gives [16 15 ... 1 0 1 ... 15 16]
K/    % Divide by 4, element-wise: gives [4. 3.75 ... 0.25 0 0.25 ... 3.75 4]
t!    % Duplicate, transpose
Yy    % Hypotenuse (square root of sum of squares), with implicit expansion.
      % Gives a square matrix
Y,    % Sine, element-wise
lYH   % Plot as surface