# zorbz - generative art with Zora's new API 

By [sweetman](https://paragraph.com/@sweetman) · 2022-06-12

---

In this you’ll learn step-by-step how we built this hackathon project from scratch.

*   [zorbz app](https://zorbz.vercel.app/).
    
*   [github](https://github.com/SweetmanTech/zorbz) (open-source).
    

![](https://storage.googleapis.com/papyrus_images/358f2357975fa85793004a4d6134ef60.png)

The Zora API Hackathon
----------------------

There have been a few new web3 releases recently:

1.  [Zora API](https://docs.zora.co/docs/zora-api/intro).
    
2.  [Mint Songs V2](https://www.mintsongs.com/) - Built on [Zora V3](https://etherscan.io/address/0x9458e29713b98bf452ee9b2c099289f533a5f377).
    
3.  [Zora Hackathon.](https://twitter.com/ZORAEngineering/status/1536026138350211072?s=20&t=R_bw4AenmYE-bh1_Trkyqg)
    

[https://twitter.com/ourZORA/status/1534959028031303681?s=20&t=R\_bw4AenmYE-bh1\_Trkyqg](https://twitter.com/ourZORA/status/1534959028031303681?s=20&t=R_bw4AenmYE-bh1_Trkyqg)

My Team
-------

It started with me making a tiny post in the Zora Discord. Valcaholics reached out to me in dms. I invited Wayne. The rest is history. Here’s our squad:

*   [valcoholics.eth](https://etherscan.io/address/0x9798a181e731ee9d3abffc5ae15c93ca3d95a8e3) - Project lead, chief architect, P5js designer.
    
*   [wayneh.eth](https://etherscan.io/address/0xc516f561098cea752f06c4f7295d0827f1ba0d6c) - smart contract engineer (solidity).
    
*   LouLouBey - design.
    
*   [sweetman.eth](https://etherscan.io/address/0xcfbf34d385ea2d5eb947063b67ea226dcda3dc38) - Zora API + Rainbowkit + metadata.
    

What we built
-------------

Here’s the our hackathon submission:

*   Front-End Template - M1guel’s [starter dApp](https://github.com/m1guelpf/dapp-starter).
    
    *   NextJS + P5js - `window` undefined error.
        
*   [P5js](https://p5js.org/) - generative art code.
    
*   [Zora API](https://api.zora.co/).
    
    *   zorb `tokens` query.
        
    *   marketplace `events` query.
        
*   [RainbowKit](https://www.rainbowkit.com/).
    
*   Minting functionality.
    

### Getting Started

I’ve been really liking some of the apps [M1guel](https://twitter.com/m1guelpf) has been releasing lately for Lens Protocol. I noticed he has a [dapp-starter project](https://github.com/m1guelpf/dapp-starter) that uses NextJS + Rainbowkit.

> PS: You can skip ahead and use our [finished code here](https://github.com/SweetmanTech/zorbz).

    git clone git@github.com:m1guelpf/dapp-starter.git
    cd dapp-starter
    yarn install
    yarn dev
    

Open [http://localhost:3000](http://localhost:3000/) with your browser to see the result.

### P5js - Generative code.

If you’re not familiar with [P5js](https://p5js.org/) it’s a common generative art library. You can learn more and use their easy-editor [here](https://p5-demos.glitch.me/).

this is the initial sketch valcoholics.eth made with one, repeated, zorb.

    import React, { useState } from 'react'
    import BaseSketch from 'react-p5'
    
    const windowWidth = 500
    const windowHeight = 500
    let x = 50
    let y = 50
    const Sketch = props => {
        const [t, setT] = useState(0)
        const [zorb, setZorb] = useState()
    
        const setup = (p5, canvasParentRef) => {
            const canvas = p5.createCanvas(windowWidth, windowHeight).parent(canvasParentRef)
        }
    
        const draw = p5 => {
            const steps = 0.005
            setT((t += steps))
            const fluid = 0.01 //slider.value()*fx/5;
            const r = 24 //slider2.value();
            const w = 102
            const h = 102
            const mvx = 20
            const mvy = 20
            p5.background(0, w)
            for (x = 0; x < windowWidth; x += 25)
                for (y = 0; y < windowHeight; y += 20) {
                    const n = _ => {
                        return p5.TAU * (t + p5.sin(p5.TAU * t - p5.dist(x, y, w / 2, h / 2) * fluid))
                    }
                    const ox = x + mvx * p5.sin(n())
                    const oy = y + mvy * p5.cos(n())
    
                    let nz = 100
                    nz = p5.noise(x * fluid, y * fluid)
                    if (zorb) {
                        p5.image(zorb, ox, oy, r, r)
                    }
                }
        }
    
        const preload = p5 => {
            const testZorb = p5.loadImage(
                'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTAgMTEwIj48ZGVmcz48cmFkaWFsR3JhZGllbnQgaWQ9Imd6ciIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSg2Ni40NTc4IDI0LjM1NzUpIHNjYWxlKDc1LjI5MDgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMSIgY3g9IjAiIGN5PSIwJSI+PHN0b3Agb2Zmc2V0PSIxNS42MiUiIHN0b3AtY29sb3I9ImhzbCgzMjYsIDczJSwgOTQlKSIgLz48c3RvcCBvZmZzZXQ9IjM5LjU4JSIgc3RvcC1jb2xvcj0iaHNsKDMyNSwgNzklLCA4NyUpIiAvPjxzdG9wIG9mZnNldD0iNzIuOTIlIiBzdG9wLWNvbG9yPSJoc2woMzE5LCA4OCUsIDc0JSkiIC8+PHN0b3Agb2Zmc2V0PSI5MC42MyUiIHN0b3AtY29sb3I9ImhzbCgzMTcsIDkyJSwgNjQlKSIgLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9ImhzbCgzMTYsIDkyJSwgNjMlKSIgLz48L3JhZGlhbEdyYWRpZW50PjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1LDUpIj48cGF0aCBkPSJNMTAwIDUwQzEwMCAyMi4zODU4IDc3LjYxNDIgMCA1MCAwQzIyLjM4NTggMCAwIDIyLjM4NTggMCA1MEMwIDc3LjYxNDIgMjIuMzg1OCAxMDAgNTAgMTAwQzc3LjYxNDIgMTAwIDEwMCA3Ny42MTQyIDEwMCA1MFoiIGZpbGw9InVybCgjZ3pyKSIgLz48cGF0aCBzdHJva2U9InJnYmEoMCwwLDAsMC4wNzUpIiBmaWxsPSJ0cmFuc3BhcmVudCIgc3Ryb2tlLXdpZHRoPSIxIiBkPSJNNTAsMC41YzI3LjMsMCw0OS41LDIyLjIsNDkuNSw0OS41Uzc3LjMsOTkuNSw1MCw5OS41UzAuNSw3Ny4zLDAuNSw1MFMyMi43LDAuNSw1MCwwLjV6IiAvPjwvZz48L3N2Zz4='
            )
            setZorb(testZorb)
        }
        return <BaseSketch setup={setup} draw={draw} preload={preload} />
    }
    
    export default Sketch
    

### NextJS + P5js - window undefined error.

NextJS is server-side rendered. Meaning, when the page is initially loaded, it doesn’t have access to `window`. Window is what P5js uses to draw on the screen. Luckily NextJS makes it easy to skip the SSR and render P5js code easily:

    import dynamic from 'next/dynamic'
    
    const DynamicComponentWithNoSSR = dynamic(() => import('../components/Sketch'), { ssr: false })
    
    const HomePage = () => (<DynamicComponentWithNoSSR zorbs={zorbs} zoraEvents={zoraEvents} />)
    
    export default HomePage
    

### Checking In - What you should have so far

![one zorb repeated in the P5js animation](https://images.mirror-media.xyz/publication-images/vLklPJT9owMRMu5sTbF51.png?height=1000&width=1000 "null")

### Zora API

We wanted to use the Zora API for 2 things:

*   zorb `tokens` query - **get zorbs** for our P5js animation.
    
*   marketplace `events` query - **1 zorb = 1 marketplace event**.
    

How to use Zora API to get an array of zorb `tokens` images:

    import { ZDK } from '@zoralabs/zdk'
    const API_ENDPOINT = 'https://api.zora.co/graphql'
    const zdk = new ZDK({ endpoint: API_ENDPOINT })
    
    const args = { where: {
    // zorb smart contract address
    collectionAddresses: ['0xCa21d4228cDCc68D4e23807E5e370C07577Dd152']
    }}
    
    // query Zora API
    const response = await zdk.tokens(args)
    
    // get zorb images
    let zorbz = response.tokens.nodes.map(zorb => zorb.token.image.url)
    

How to get `events` from Zora for a single day:

    // date ex. "2022-01-01" 
    const eventsArgs = (startDate, endDate) => ({
            where: {},
            filter: { timeFilter: { endDate, startDate }, eventTypes: 'V2_AUCTION_EVENT' },
            pagination: { limit: 500 },
    })
    
    const dailyEventArgs = eventsArgs(params.startDate, params.endDate)
    const response = await zdk.events(dailyEventArgs)
    const zoraEvents = response.events.nodes
    

### Checking In - What you should have so far

![moar zorbz](https://images.mirror-media.xyz/publication-images/x-4QbEDmyOr8Jk8WBDb2U.png?height=1000&width=1000 "null")

### Rainbowkit

[Rainbowkit](https://www.rainbowkit.com/) is a beautiful & easy-to-use wallet interface for dApp developers. Remember how we used the [dapp-starter project](https://github.com/m1guelpf/dapp-starter)? That includes Rainbowkit out of the box.

To add Rainbowkit, all we need to do is put in a `<ConnectWallet />`:

`src/pages/[tokenId].js`

    import ConnectWallet from '../components/ConnectWallet'
    
    const TokenPage = () = (
    ...		
        <ConnectWallet />
    ....
    )
    
    export default TokenPage.
    

> PS: add `coolMode` for the added effects.

![Rainbowkit](https://images.mirror-media.xyz/publication-images/tEz51XYJVyftganT4OtQn.png?height=1800&width=2880 "null")

### Minting

You can mint from the zorbz project here:

[https://zorbz.vercel.app/](https://zorbz.vercel.app/)

Rainbowkit leverages the `wagmi` package for smart contract calls. Here’s how we use `wagmi` for simple tasks such as:

minting an NFT ([abi](https://ropsten.etherscan.io/address/0x88d18451249d121A28637E4bE0B6BF7738729013#code)).

    import { useContract, useSigner } from 'wagmi'
    import abi from './abi.json'
    
    const TokenPage = () => {
      const { data: signer } = useSigner()
      const contract = useContract({
        addressOrName: '0x88d18451249d121A28637E4bE0B6BF7738729013',
        contractInterface: abi,
        signerOrProvider: signer,
      })
    
      const mintNft = async () => {
        await contract.mint( 
            1,
            'ipfs://QmfJZn2nA1FsSNgb2mQmvAM9sAvviQZHonivQB5bMwuFQX'
        );
      }
    
      return <button onClick={mintNft}>Mint</button>
    };
    
    export default TokenPage;
    

Your finished [product](https://zorbz.vercel.app/).

![](https://images.mirror-media.xyz/publication-images/FFowvU0dcDgkM-ezCLbQF.png?height=1434&width=2880 "null")

Let me know if you have any questions on [Lens](https://lenster.xyz/u/sweetman.lens) or [Twitter](https://twitter.com/sweetman_eth).

---

*Originally published on [sweetman](https://paragraph.com/@sweetman/zorbz-generative-art-with-zoras-new-api)*
