{"name":"dev_PARTY #014","description":"Interactive NFT by @andriibakulin\n\n<code>\nconst ITEMS_COUNT = 40;\n\nlet gEntities = [];\nlet gSizeArea;\nlet gSizePoints;\nlet gDistance, gDistanceSqr;\n\n// MagicLogic\n\nclass Entity\n{\n    constructor(index, count, xOffset, yOffset, direction)\n    {\n        const seq01 = index / count;\n\n        this.xOffset = xOffset;\n        this.yOffset = yOffset;\n        this.direction = direction;\n\n        this.color  = seq01 * 255;\n        this.offset = seq01 * Math.PI;\n        this.angel  = seq01 * Math.PI * 2;\n\n        this.power = new SmoothValue();\n\n        // Dynamic vars\n        this.circleX = this.circleY = null;\n        this.drawAtX = this.drawAtY = null;\n        this.colorV = null;\n\n        this.tick = 0;\n    }\n\n    next(dt)\n    {\n        this.tick++;\n        this.color  += dt * 128;\n        this.offset += dt;\n        this.angel  -= dt / 2;\n\n        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n        // Update dynamic attrs\n\n        const radius = gSizeArea * 0.15;\n\n        this.circleX = Math.sin(this.angel * this.direction) * radius;\n        this.circleY = Math.cos(this.angel * this.direction) * radius;\n\n        this.drawAtX = gSizeArea * this.xOffset + this.circleX;\n        this.drawAtY = gSizeArea * this.yOffset + this.circleY;\n\n        this.colorV = normalizeColorComp(this.color);\n\n        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n        let dstOffset = 0;\n\n        if (mouseIsPressed)\n        {\n            const dstLengthSqr = (mouseX-this.drawAtX)**2 + (mouseY-this.drawAtY)**2;\n\n            if (dstLengthSqr < gDistanceSqr)\n                dstOffset = Math.sqrt(dstLengthSqr) / gDistance;\n        }\n\n        if (dstOffset > 0)\n            this.power.update(1 - dstOffset, 0.5 * dt);\n        else\n            this.power.update(0, 2 * dt);\n    }\n\n    render()\n    {\n        let offset = -this.power.v * 2 * cos(this.offset);\n\n        const winCenterX = gSizeArea/2 - this.drawAtX;\n        const winCenterY = gSizeArea/2 - this.drawAtY;\n\n        resetMatrix();\n        translate(this.drawAtX, this.drawAtY);\n\n        strokeWeight(gSizePoints);\n        stroke(this.colorV, 255, 255);\n        point(0,0);\n\n        for (let i=-8; i<=8; i++)\n        {\n            let vv = 0.2 * i;\n            let xx = this.circleX * offset * vv;\n            let yy = this.circleY * offset * vv;\n\n            if (abs(xx) < 5 && abs(yy) < 5)\n                continue;\n\n            if (i===5)\n            {\n                let alpha = this.power.v*255;\n                if (alpha > 0)\n                {\n                    strokeWeight(3);\n                    stroke(this.colorV, 255, 255, alpha);\n                    line(winCenterX,winCenterY, xx,yy);\n                }\n            }\n\n            strokeWeight(5-Math.abs(i/2));\n            stroke(this.colorV, 255, 255, (this.power.v+0.3)*255);\n            point(xx, yy);\n        }\n    }\n}\n\n// Lifecycle\n\nfunction setup()\n{\n    updateConsts();\n    createCanvas(gSizeArea, gSizeArea);\n    background(0);\n\n    pixelDensity(1);\n    frameRate(60);\n    colorMode(HSB, 255);\n    angleMode(DEGREES);\n\n    const dlt = 0.21;\n    for (let index=0; index<ITEMS_COUNT; index++)\n    {\n        gEntities.push(new Entity(index, ITEMS_COUNT, 0.50-dlt, 0.50-dlt, +1));\n        gEntities.push(new Entity(index, ITEMS_COUNT, 0.50+dlt, 0.50-dlt, -1));\n        gEntities.push(new Entity(index, ITEMS_COUNT, 0.50-dlt, 0.50+dlt, -1));\n        gEntities.push(new Entity(index, ITEMS_COUNT, 0.50+dlt, 0.50+dlt, +1));\n    }\n}\n\nfunction windowResized()\n{\n    updateConsts();\n    resizeCanvas(gSizeArea, gSizeArea);\n}\n\nfunction draw()\n{\n    const dt = deltaTime/1000;\n\n    background(0);\n\n    for (const idx in gEntities)\n    {\n        const ent = gEntities[idx];\n        ent.next(dt);\n        ent.render();\n    }\n}\n\n// Helpers\n\nfunction updateConsts()\n{\n    gSizeArea     = getAreaSize();\n    gDistance     = gSizeArea / 5;\n    gDistanceSqr  = gDistance ** 2;\n    gSizePoints   = clamp(gSizeArea / 66, 5, 12);\n}\n\nfunction normalizeColorComp(v)\n{\n    v %= 256;\n    return v >= 0 ? v : v+256;\n}\n\nfunction getAreaSize()\n{\n    return Math.min(window.innerWidth, window.innerHeight);\n}\n\nfunction clamp(value, min, max)\n{\n    if (value < min) return min;\n    if (value > max) return max;\n    return value;\n}\n\n// Helpers : Smooth\n\nfunction smoothValue(v0, v1, delta)\n{\n    if (v0 === null)\n        return v1;\n\n    const dv = v1 - v0;\n    return Math.abs(dv) <= delta ? v1 : (dv < 0 ? v0 - delta : v0 + delta);\n}\n\nclass SmoothValue\n{\n    constructor(v=null)\n    {\n        this.v = v;\n    }\n\n    update(v, delta)\n    {\n        this.v = smoothValue(this.v, v, delta);\n    }\n}\n</code>\n","tags":["genart","codeart","interactive","procedural","generative","realtime","abstract","colorful","art","algorithmic","script","p5js","js","math"],"symbol":"OBJKT","artifactUri":"ipfs://QmZagFLrT6dmbzkYqi1SGHxEHG7yFFK2BRKs5fX2qLaFKj","displayUri":"ipfs://QmQaquYwruTRwhkdrrgYWftD7atCYjKGJEh88Hdqug4RyN","thumbnailUri":"ipfs://QmNrhZHUaEqxhyLfqoq1mtHSipkWHeT31LNHb1QEbDHgnc","creators":["tz1Ys42frYhgxHxXtqoY42GiuRPZ9ykbP86Y"],"formats":[{"uri":"ipfs://QmZagFLrT6dmbzkYqi1SGHxEHG7yFFK2BRKs5fX2qLaFKj","mimeType":"application/x-directory"}],"decimals":0,"isBooleanAmount":false,"shouldPreferSymbol":false}