eye art

Pyschedelic Eye Art With React

Here's the thing about boring tutorials: you're bored of reading them, I'm bored of writing them.

So here's something out of the ordinary.


The basic concept is to use react-bootstrap, a UI library, to start with a simple unit - you can think of it as an eye emoji - and keep 'stepping' it up, line by line, until you have a half-screen full of increasing numbers of emoji on each line.

Then you do the same thing in the reverse direction, ending at 0.

Then, because the best part of this is being able to change it and see the changes in action, there's a button that changes the total number of lines, so you can see the number of emoji (with colors) shrink or expand, according to your preference.

Structure of the React App

There's a lot going on here, but I'll do my best to break it into its constituent steps. Also, practically every unit here corresponds to something you can see, and that makes it easier.

There's useState, which holds the total number of rows, so the button can change it (otherwise, it could just be a constant - no state, or useState, needed).

The way that app works is as follows.

We start with this:

const upOrder=[...Array(total/2).keys()]

For a value of total of 20, it creates an array that looks like this:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

This get used in a component which goes through that array, number by number. Each number represents a 'row' of eyes.

The program moves through the array. 1, 2, 3, 4, 5...

Let's say it's at 5, to show what it's doing.

At 5, there's a component that creates a new array, that goes from 0 to 5: [0,1,2,3,4,5].

Then it draws, on the screen, a row with that many elements, dropping an eye emoji in each one, and coloring it according to a selection from the range of allowable colors you gave it.

Then we back to the component I mentioned at the top, which moves on to the next number, 6, and repeats the process - new row with one element each for [0,1,2,3,4,5,6] with an eye emoji and a color.

And so on all the way up to the top number - here, 19. At this point, there's 19 rows with emoji eyes on each one, with 1 emoji eye in the first row and 19 emoji eyes in the last row.

Then the program starts up again from the top, this time using a different array:

[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

And that gets you your screen of eye weirdness, shown below.

Code Breakdown

This app, in terms of written code, is basically just one file, App.js.

Each component may be short, but each component does a lot of work, so it really makes sense to take it line by line.

function App()

const [total,setTotal]=useState(40);

const lowerBound=4;

const upperBound=100;

const clr=["red"];

const upcolors=["red","blue"]

const downcolors=["teal","pink","green","silver","brown","purple","gold"]

return (

<div style={{backgroundColor:'red',height:'100vh'}}>

<div style={{position: 'sticky', top: '0',zIndex:'1'}}>

<Button variant="light" onClick={()=>{if (total<=upperBound) { setTotal(total+2) }}}>Increase</Button>

<Button variant="dark" onClick={()=>{ if (total>=lowerBound) { setTotal(total-2) }}}>Decrease</Button>


<LayerWall lines={[...Array(total/2).keys()]} colorRange={upcolors}/>

<LayerWall lines={[...Array(total/2).keys()].reverse()} colorRange={downcolors}/>




export default App;

Constants up top, then the button where you can change the total number of rows, buttons, and then the row of eyes: one LayerWall to form the upper half of the circle, one LayerWall to form the lower half of the circle.

The button changes the number of rows. That's why you have to have 'state' - so that the screen can be redrawn as a result of updates to it.

I have an 'upperBound' and a 'lowerBound' there - upperBound so the screen doesn't become a garbled mess of too many eye emoji, and a lowerBound so there can't be fewer than that many rows on the screen. (Also, if it goes below 0, the program will crash, since it doesn't know what to do with an array of size smaller than 0).

When you press the button to increase, it increases the number of rows by 2. When the you press the button to decrease, it decreases it by the same amount.

The reason it's 2, and not 1, is because the number of rows has to be divisible by 2, since we divide the number in half to do the upward-ascending and downward-ascending pyramids of eyes.


There's a few components here: LayerWall, Layer, and Unit.

LayerWall is half of it: one LayerWall builds the upper half, one LayerWall builds the lower half. I did it in two pieces so they could be separately colored, as they are in the screenshot.

Layer is basically the Row of Eyes.

Unit is each eye: the small colored container it is in, and the height. This varies based on the number of rows in all, so that basically no matter how many or how few, it fills the size of one screen, designated as 100vh in css.

The vh stands for vertical height, and by convention, 100vh equals the size of whatever screen you are viewing it on (the css processor handles that math).

Downloading the Code

That's all there is to it.

You can see the app in action here:


The GitHub repo is here:


Enjoy! But be careful: the 👁️ is watching you.