### Fuzzy lighting

Circular light source + multiple rays per point

```const light_Y = 25; // min=0, max=35, step=1
const light_R = 20; // min=1, max=25, step=1
const N_points = 1000000;
const stroke_len = 1;
const N_rays = 16; // min=1, max=64, step=1
const fade = 1; // min=0, max=1, step=1 (False, True)
const max_D = 170; // min=100, max=250, step= 1
const show_light = 1; // min=0, max=1, step=1 (True circle source, Candle)

let R = light_R;
let R2 = R * R;

Canvas.setpenopacity(-0.5);

const t = new Turtle();

const light = [0, -light_Y];

const walls = [[[-75, -25], [-75, 25]], [[-80, -25], [-75, -25]],
[[75, -25], [75, 25]], [[75, -25], [80, -25]],
[[-25, -75], [25, -75]],
[[-25, 50], [25, 50]],
[[-75, 75], [-50, 75]], [[-50, 75], [-50, 100]],
[[75, 75], [50, 75]], [[50, 75], [50, 100]]];

for (let i = 0; i < walls.length; i++) {
if (walls[i][0][0] == walls[i][1][0]) {
[walls[i][0][1], walls[i][1][1]] = [Math.min(walls[i][0][1], walls[i][1][1]), Math.max(walls[i][0][1], walls[i][1][1])]
}
else {
[walls[i][0][0], walls[i][1][0]] = [Math.min(walls[i][0][0], walls[i][1][0]), Math.max(walls[i][0][0], walls[i][1][0])]
}
}

if (show_light == 0) { // draw true light circle
for (let r = 0.1; r <= R; r += 0.05) {
t.jump(light[0] - r, light[1]);
t.seth(-90);
t.circle(r);
}
}
else { // draw candle
let fire_r = Math.max(2, light_R / 5);

for (let r = 0.1; r <= fire_r; r += 0.05) {
t.jump(light[0] - r, light[1]);
t.seth(-90);
t.circle(r);
}

let fire_h = 2.5 * fire_r;

for (let dy = 0; dy <= fire_h; dy += 0.05) {
let dx = fire_r * (1 - dy / fire_h)**0.6;
let y = light[1] - dy - 1;
let dy1 = light[1] - y;

if (dy1 <= fire_r) {
let dr = Math.sqrt(fire_r**2 - dy1**2);

t.jump(-dx, y);
t.goto(-dr, y);

t.jump(+dr, y);
t.goto(+dx, y);
}
else {
t.jump(-dx, y);
t.goto(+dx, y);
}
}

let candle_r = 4;
let candle_y = light[1] + fire_r + 1

for (let y = candle_y; y < 50; y += 0.05) {
t.jump(-candle_r, y);
t.goto(+candle_r, y);
}

for (let x = -0.5; x <= 0.5; x += 0.05) {
t.jump(x, candle_y);
t.goto(x, light[1] + fire_r);
}
}

function intersects(p1, p2, w1, w2) {
if (w1[0] == w2[0]) {
if (((p1[0] < w1[0]) && (p2[0] < w1[0])) || ((p1[0] > w1[0]) && (p2[0] > w1[0]))) {
return false;
}
else {
let y = p1[1] + (p2[1] - p1[1]) / (p2[0]- p1[0]) * (w1[0] - p1[0]);

return (w1[1] < y) && (y < w2[1]);
}
}
else {
if (((p1[1] < w1[1]) && (p2[1] < w1[1])) || ((p1[1] > w1[1]) && (p2[1] > w1[1]))) {
return false;
}
else {
let x = p1[0] + (p2[0] - p1[0]) / (p2[1]- p1[1]) * (w1[1] - p1[1]);

return (w1[0] < x) && (x < w2[0]);
}
}
}

function walk(frame) {
let x = -100 + 200 * Math.random();
let y = -100 + 200 * Math.random();
let p = [x, y];

let dx = light[0] - x, dy = light[1] - y;
let d2 = dx * dx + dy * dy;

let n = 0;

for (let i = 0; i < N_rays; i++) {
let phi = 2 * Math.PI * Math.random();
let light_p = [light[0] + R * Math.cos(phi), light[1] + R * Math.sin(phi)];

let ok = true;
for (let w of walls) {
if (intersects(light_p, p, w[0], w[1])) {
ok = false;
break;
}
}

if (ok) {
n++;
}
}

if (n > 0) {
let d = Math.sqrt(d2);
let s = stroke_len / d * (n / N_rays);