# zorbz - generative art with Zora's new API
**Published by:** [sweetman](https://paragraph.com/@sweetman/)
**Published on:** 2022-06-12
**URL:** https://paragraph.com/@sweetman/zorbz-generative-art-with-zoras-new-api
## Content
In this you’ll learn step-by-step how we built this hackathon project from scratch.zorbz app.github (open-source).The Zora API HackathonThere have been a few new web3 releases recently:Zora API.Mint Songs V2 - Built on Zora V3.Zora Hackathon.https://twitter.com/ourZORA/status/1534959028031303681?s=20&t=R_bw4AenmYE-bh1_TrkyqgMy TeamIt 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 - Project lead, chief architect, P5js designer.wayneh.eth - smart contract engineer (solidity).LouLouBey - design.sweetman.eth - Zora API + Rainbowkit + metadata.What we builtHere’s the our hackathon submission:Front-End Template - M1guel’s starter dApp.NextJS + P5js - window undefined error.P5js - generative art code.Zora API.zorb tokens query.marketplace events query.RainbowKit.Minting functionality.Getting StartedI’ve been really liking some of the apps M1guel has been releasing lately for Lens Protocol. I noticed he has a dapp-starter project that uses NextJS + Rainbowkit.PS: You can skip ahead and use our finished code here.git clone git@github.com:m1guelpf/dapp-starter.git cd dapp-starter yarn install yarn dev Open http://localhost:3000 with your browser to see the result.P5js - Generative code.If you’re not familiar with P5js it’s a common generative art library. You can learn more and use their easy-editor here. 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 } 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 = () => () export default HomePage Checking In - What you should have so farZora APIWe 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 farRainbowkitRainbowkit is a beautiful & easy-to-use wallet interface for dApp developers. Remember how we used the dapp-starter project? That includes Rainbowkit out of the box. To add Rainbowkit, all we need to do is put in a : src/pages/[tokenId].jsimport ConnectWallet from '../components/ConnectWallet' const TokenPage = () = ( ... .... ) export default TokenPage. PS: add coolMode for the added effects.MintingYou can mint from the zorbz project here: 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).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 }; export default TokenPage; Your finished product.Let me know if you have any questions on Lens or Twitter.
## Publication Information
- [sweetman](https://paragraph.com/@sweetman/): Publication homepage
- [All Posts](https://paragraph.com/@sweetman/): More posts from this publication
- [RSS Feed](https://api.paragraph.com/blogs/rss/@sweetman): Subscribe to updates
## Optional
- [Collect as NFT](https://paragraph.com/@sweetman/zorbz-generative-art-with-zoras-new-api): Support the author by collecting this post
- [View Collectors](https://paragraph.com/@sweetman/zorbz-generative-art-with-zoras-new-api/collectors): See who has collected this post