├── .gitignore ├── .npmignore ├── 1.html ├── 11.html ├── 13.html ├── 14.html ├── 15.html ├── 16.html ├── 17.html ├── 19.html ├── 2.html ├── 20.html ├── 21.html ├── 22.html ├── 23.html ├── 26.html ├── 27.html ├── 28.html ├── 29.html ├── 3.html ├── 30.html ├── 5.html ├── 6.html ├── 7.html ├── 8.html ├── 9.html ├── LICENSE.md ├── README.md ├── assets ├── 0.jpg ├── 1.jpg ├── 14-lut.png ├── 15-lut.png ├── 2.jpg ├── 3.jpg ├── KelsonSans.fnt ├── KelsonSans.png ├── apartment │ ├── Apartment_Diffuse.hdr │ ├── Apartment_Reflection.hdr │ ├── Apartment_Spherical_HiRes.jpg │ ├── Camels_Icon.jpg │ └── apartment.ibl ├── audio.svg ├── bluejean.mp3 ├── bluejean_short.mp3 ├── burlap-normals.jpg ├── complete_hrtfs.json ├── cracks.jpg ├── dust.jpg ├── entypo-svg-paths.json ├── feather.svg ├── glass.jpg ├── grunge.jpg ├── grunge2.jpg ├── highroad.mp3 ├── iphone.svg ├── metal-normals.jpg ├── metal2-normals.png ├── noise.png ├── paper.png ├── paper1-diffuse.jpg ├── play.svg ├── soul.mp3 ├── soul_short.mp3 ├── thumb-palettes.json ├── thumbs │ ├── mattdesl-codevember-1.jpg │ ├── mattdesl-codevember-10.jpg │ ├── mattdesl-codevember-11.jpg │ ├── mattdesl-codevember-12.jpg │ ├── mattdesl-codevember-13.jpg │ ├── mattdesl-codevember-14.jpg │ ├── mattdesl-codevember-15.jpg │ ├── mattdesl-codevember-16.jpg │ ├── mattdesl-codevember-17.jpg │ ├── mattdesl-codevember-18.jpg │ ├── mattdesl-codevember-19.jpg │ ├── mattdesl-codevember-2.jpg │ ├── mattdesl-codevember-20.jpg │ ├── mattdesl-codevember-21.jpg │ ├── mattdesl-codevember-22.jpg │ ├── mattdesl-codevember-23.jpg │ ├── mattdesl-codevember-24.jpg │ ├── mattdesl-codevember-25.jpg │ ├── mattdesl-codevember-26.jpg │ ├── mattdesl-codevember-27.jpg │ ├── mattdesl-codevember-28.jpg │ ├── mattdesl-codevember-29.jpg │ ├── mattdesl-codevember-3.jpg │ ├── mattdesl-codevember-30.jpg │ ├── mattdesl-codevember-4.jpg │ ├── mattdesl-codevember-5.jpg │ ├── mattdesl-codevember-6.jpg │ ├── mattdesl-codevember-7.jpg │ ├── mattdesl-codevember-8.jpg │ └── mattdesl-codevember-9.jpg └── vu.png ├── build.js ├── config.js ├── dev.js ├── ideas └── redux.md ├── index.html ├── package.json ├── src ├── 1.js ├── 11.js ├── 13.js ├── 14.js ├── 15.js ├── 16.js ├── 17.js ├── 19.js ├── 2.js ├── 20.js ├── 21.js ├── 22.js ├── 23.js ├── 26.js ├── 27.js ├── 28.js ├── 29.js ├── 3.js ├── 30.js ├── 5.js ├── 6.js ├── 7.js ├── 8.js ├── 9.js ├── audio-util.js ├── desktop-only.js ├── fatal-error.js ├── geom-test.js ├── gl │ ├── 30-scene.js │ ├── DeviceOrientationControls.js │ ├── FontUtils.js │ ├── ShapeUtils.js │ ├── TextGeometry.js │ ├── ThreeLine25D.js │ ├── gl-line-3d.js │ ├── helvetiker.typeface.js │ ├── motion.js │ ├── optimer_regular.typeface.js │ ├── texel-coord-solid-angle.js │ ├── three-face-centroid.js │ ├── three-hdr-texture.js │ ├── three-skybox.js │ ├── three-streetview-app.js │ ├── three-vignette-background.js │ ├── unindex-geometry.js │ ├── xyz-pixels-to-rgb.js │ └── xyz-pixels-to-rgbm.js ├── grid │ ├── calendar-grid.js │ ├── content.js │ ├── grid-item.hbs │ ├── image-urls.js │ ├── index.js │ ├── main.css │ ├── palette.js │ └── sort-files.js ├── is-low-end.js ├── is-mobile.js ├── old-10.js ├── shaders │ ├── 14-post.frag │ ├── 14-post.js │ ├── 14-post.vert │ ├── 14.frag │ ├── 14.vert │ ├── 15-post.frag │ ├── 15-post.js │ ├── 15.frag │ ├── 15.vert │ ├── 17.frag │ ├── 17.vert │ ├── 21-line.frag │ ├── 21-line.vert │ ├── 23.frag │ ├── 23.vert │ ├── 27.frag │ ├── 27.vert │ ├── 28.frag │ ├── 28.vert │ ├── 29.frag │ ├── 29.vert │ ├── 3.frag │ ├── 3.vert │ ├── 5.frag │ ├── 5.vert │ ├── cube-to-uv.glsl │ ├── envmap-equirect.glsl │ ├── prefilter-cube.glsl │ ├── rgbm-decode.glsl │ ├── rgbm-encode.glsl │ ├── rotation-zyx.glsl │ ├── sdf.js │ ├── sky.frag │ ├── sky.js │ ├── src-over.glsl │ ├── texture-query-lod.glsl │ ├── tonemap-filmic.glsl │ ├── tonemap-reinhard.glsl │ └── tonemap-uncharted2.glsl ├── simplicial-with-uv.js ├── three-orbit-app.js └── util │ ├── acorn-types.js │ ├── audio-demo-2d.js │ ├── boids-3d.js │ ├── punch-card.js │ └── raven.txt └── static ├── 1.js ├── 10.js ├── 11.js ├── 13.js ├── 14.js ├── 15.js ├── 16.js ├── 17.js ├── 19.js ├── 2.js ├── 20.js ├── 21.js ├── 22.js ├── 23.js ├── 26.js ├── 27.js ├── 28.js ├── 29.js ├── 3.js ├── 30.js ├── 5.js ├── 6.js ├── 7.js ├── 8.js ├── 9.js ├── grid.js └── loader.css /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | assets/mesh 7 | ideas/*.md -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 1 - @mattdesl 7 | 15 | 16 | 67 | 68 | 69 | 70 |
71 | codevember/1 72 | click shape to break 73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /11.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 11 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /13.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 13 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /14.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 14 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /15.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 15 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 16 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /17.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 17 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /19.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 19 - @mattdesl 7 | 45 | 53 | 54 | 55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 2 - @mattdesl 7 | 15 | 16 | 38 | 39 | 40 | 41 |
42 |
codevember/2
43 | drag for action 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /20.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 20 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /21.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 21 - @mattdesl 7 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /22.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 22 - @mattdesl 7 | 8 | 47 | 55 | 56 | 57 |
58 |
A random Sun Tzu quote encoded as stylized EBCDIC punched cards.
59 |
60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /23.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 23 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /26.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 26 - @mattdesl 7 | 8 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /27.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 27 - @mattdesl 7 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /28.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 28 - @mattdesl 7 | 8 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /29.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 29 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 3 - @mattdesl 7 | 15 | 39 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /30.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 30 - @mattdesl 7 | 15 | 16 | 17 | 48 | 49 | 50 | 51 |
52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 5 - @mattdesl 7 | 15 | 16 | 30 | 31 | 32 |
click to change displacement
33 |
mouse / touch to move image
34 | 35 | 36 | -------------------------------------------------------------------------------- /6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 6 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 7 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 8 - @mattdesl 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember - 9 - @mattdesl 7 | 15 | 16 | 37 | 38 | 39 |
click anywhere
40 | 41 | 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codevember 2 | 3 | [![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) 4 | 5 | Desktop recommended for all demos. Some are mobile-friendly. 6 | 7 | See here for the full grid and basic descriptions: 8 | 9 | http://mattdesl.github.io/codevember/ 10 | 11 | ![screenshots](http://i.imgur.com/B31tgZc.jpg) 12 | 13 | ## Running 14 | 15 | Clone this repo, then: 16 | 17 | ```sh 18 | npm install 19 | 20 | # start day 1 demo 21 | npm start 1 22 | 23 | # start day 5 and launch browser 24 | npm start -- 1 --open 25 | ``` 26 | 27 | You can `npm run start -- 5 --open` to launch day 5 and open the browser to [http://localhost:9966/](http://localhost:9966/). 28 | 29 | To build all demos: 30 | 31 | ```sh 32 | npm run build 33 | ``` 34 | 35 | To build one demo: 36 | 37 | ```sh 38 | node build.js 2 39 | ``` 40 | 41 | To run main grid: 42 | 43 | ```sh 44 | npm start 45 | ``` 46 | 47 | ## License 48 | 49 | MIT, see [LICENSE.md](http://github.com/mattdesl/codevember/blob/master/LICENSE.md) for details. 50 | -------------------------------------------------------------------------------- /assets/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/0.jpg -------------------------------------------------------------------------------- /assets/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/1.jpg -------------------------------------------------------------------------------- /assets/14-lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/14-lut.png -------------------------------------------------------------------------------- /assets/15-lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/15-lut.png -------------------------------------------------------------------------------- /assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/2.jpg -------------------------------------------------------------------------------- /assets/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/3.jpg -------------------------------------------------------------------------------- /assets/KelsonSans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/KelsonSans.png -------------------------------------------------------------------------------- /assets/apartment/Apartment_Diffuse.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/apartment/Apartment_Diffuse.hdr -------------------------------------------------------------------------------- /assets/apartment/Apartment_Reflection.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/apartment/Apartment_Reflection.hdr -------------------------------------------------------------------------------- /assets/apartment/Apartment_Spherical_HiRes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/apartment/Apartment_Spherical_HiRes.jpg -------------------------------------------------------------------------------- /assets/apartment/Camels_Icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/apartment/Camels_Icon.jpg -------------------------------------------------------------------------------- /assets/apartment/apartment.ibl: -------------------------------------------------------------------------------- 1 | [Header] 2 | ; some general information for preview purpose 3 | ; Icons must be 128 by 128 pixels 4 | ICOfile = "Camels_Icon.jpg" 5 | Name = "Apartment" 6 | Author = "Blochi" 7 | Location = "Hollywood" 8 | Comment = "Inside Scene, works best for objects on table." 9 | GEOlat = 34.102992275554215 10 | GEOlong = -118.34119141101837 11 | Link = "http://www.hdrlabs.com/sibl/archive.html" 12 | Date = "2003:03:02" 13 | Time = "10:56:00" 14 | Height = 0.2 15 | North = 0.5 16 | 17 | [Background] 18 | ; The Bgfile.. normally a hires LDRimage 19 | BGfile = "Apartment_Spherical_HiRes.jpg" 20 | BGmap = 1 21 | BGu = 0 22 | BGv = 0 23 | BGheight = 1024 24 | 25 | [Enviroment] 26 | ; The Enviromentfile LowRes HDRI with Diffuse Blur 27 | EVfile = "Apartment_Diffuse.hdr" 28 | EVmap = 1 29 | EVu = 0 30 | EVv = 0 31 | EVheight = 128 32 | EVmulti = 1 33 | EVgamma = 1 34 | 35 | [Reflection] 36 | ; The ReflectionMap is a crisp MedRes HDRI 37 | REFfile = "Apartment_Reflection.hdr" 38 | REFmap = 1 39 | REFu = 0 40 | REFv = 0 41 | REFheight = 256 42 | REFmulti = 1 43 | REFgamma = 1 44 | 45 | [Sun] 46 | ; Additional keylight ..ha! 47 | SUNcolor = 240,250,255 48 | SUNmulti = 1.1 49 | SUNu = 0.625 50 | SUNv = 0.3906 51 | 52 | -------------------------------------------------------------------------------- /assets/audio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 16 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /assets/bluejean.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/bluejean.mp3 -------------------------------------------------------------------------------- /assets/bluejean_short.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/bluejean_short.mp3 -------------------------------------------------------------------------------- /assets/burlap-normals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/burlap-normals.jpg -------------------------------------------------------------------------------- /assets/cracks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/cracks.jpg -------------------------------------------------------------------------------- /assets/dust.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/dust.jpg -------------------------------------------------------------------------------- /assets/feather.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /assets/glass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/glass.jpg -------------------------------------------------------------------------------- /assets/grunge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/grunge.jpg -------------------------------------------------------------------------------- /assets/grunge2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/grunge2.jpg -------------------------------------------------------------------------------- /assets/highroad.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/highroad.mp3 -------------------------------------------------------------------------------- /assets/iphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /assets/metal-normals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/metal-normals.jpg -------------------------------------------------------------------------------- /assets/metal2-normals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/metal2-normals.png -------------------------------------------------------------------------------- /assets/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/noise.png -------------------------------------------------------------------------------- /assets/paper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/paper.png -------------------------------------------------------------------------------- /assets/paper1-diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/paper1-diffuse.jpg -------------------------------------------------------------------------------- /assets/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/soul.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/soul.mp3 -------------------------------------------------------------------------------- /assets/soul_short.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/soul_short.mp3 -------------------------------------------------------------------------------- /assets/thumb-palettes.json: -------------------------------------------------------------------------------- 1 | [[[227,105,113],[249,234,235],[230,118,160]],[[225,224,224],[89,33,36],[122,69,72]],[[246,248,250],[79,115,160],[175,77,174]],[[61,62,72],[246,245,246],[140,164,178]],[[247,247,247],[63,51,51],[137,94,81]],[[4,4,4],[219,219,219],[124,124,124]],[[52,171,235],[209,237,248],[127,203,240]],[[219,124,44],[246,229,208],[227,167,117]],[[240,240,240],[91,91,91],[124,124,124]],[[63,65,74],[246,247,247],[129,175,175]],[[69,57,50],[202,168,136],[146,159,146]],[[49,51,58],[215,209,206],[124,168,183]],[[226,224,215],[64,184,133],[211,127,64]],[[41,28,35],[134,64,172],[87,45,103]],[[40,39,41],[163,149,140],[129,121,124]],[[251,251,251],[28,28,28],[192,51,50]],[[60,12,60],[141,65,112],[120,73,108]],[[242,242,242],[29,29,29],[124,124,124]],[[42,56,59],[191,195,192],[118,138,170]],[[230,228,226],[47,45,44],[116,108,108]],[[226,224,227],[208,113,204],[107,164,222]],[[250,250,249],[203,177,114],[79,79,70]],[[240,240,240],[62,62,62],[124,124,124]],[[251,251,251],[4,4,4],[124,124,124]],[[58,61,73],[218,213,218],[84,188,197]],[[171,170,172],[76,70,60],[97,87,76]],[[215,97,36],[248,241,234],[129,40,17]],[[212,215,217],[78,74,57],[129,127,89]],[[92,118,135],[209,216,219],[169,189,203]],[[239,240,241],[21,21,21],[116,116,116]]] 2 | -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-1.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-10.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-11.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-12.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-13.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-14.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-15.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-16.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-17.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-18.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-19.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-2.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-20.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-21.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-22.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-23.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-24.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-25.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-26.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-27.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-28.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-29.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-3.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-30.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-4.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-5.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-6.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-7.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-8.jpg -------------------------------------------------------------------------------- /assets/thumbs/mattdesl-codevember-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/thumbs/mattdesl-codevember-9.jpg -------------------------------------------------------------------------------- /assets/vu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/assets/vu.png -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | global.Promise = require('pinkie-promise') 2 | const argv = require('minimist')(process.argv.slice(2)) 3 | const browserify = require('browserify') 4 | const fs = require('fs') 5 | const path = require('path') 6 | const UglifyJS = require('uglify-js') 7 | 8 | var files = fs.readdirSync(path.resolve(__dirname, 'src')) 9 | files = files.filter(function (f) { 10 | return /^\d+\.js$/.test(f) 11 | }) 12 | 13 | const transformConfig = require('./config') 14 | 15 | var entry = argv._[0] 16 | if (typeof entry === 'number') { 17 | entry = String(entry) 18 | } else if (!entry) { 19 | entry = 'grid' 20 | } 21 | 22 | var isGrid = entry === 'grid' 23 | 24 | if (entry && isGrid) { 25 | files = [ path.join('grid', 'index.js') ] 26 | } else if (entry) { 27 | files = files.filter(function (f) { 28 | return path.basename(f, '.js') === entry 29 | }) 30 | } 31 | 32 | Promise.all(files.map(runBuild)).catch(function (err) { 33 | console.error(err) 34 | }).then(function () { 35 | console.log("Finished") 36 | }) 37 | 38 | function runBuild (f) { 39 | return new Promise(function (resolve, reject) { 40 | console.log('Bundling', f) 41 | var b = browserify('src/' + f, { 42 | debug: false, 43 | noparse: [ 'three' ] 44 | }) 45 | b.transform(require('babelify').configure({ presets: 'es2015' })) 46 | b.plugin(require('bundle-collapser/plugin')) 47 | var base = isGrid ? 'grid' : path.basename(f, '.js') 48 | if (isGrid) f = 'grid.js' 49 | var transforms = (transformConfig[base] || []) 50 | transforms.forEach(function (t) { 51 | b.transform(t) 52 | }) 53 | b.bundle(function (err, src) { 54 | if (err) return reject(err) 55 | console.log('Compressing', f) 56 | var result = UglifyJS.minify(src.toString(), { fromString: true }) 57 | console.log('Writing', f) 58 | fs.writeFile('static/' + f, result.code, function (err) { 59 | if (err) return reject(err) 60 | resolve() 61 | }) 62 | }) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '3': ['glslify'], 3 | '5': ['glslify'], 4 | '11': ['glslify'], 5 | '14': ['glslify'], 6 | '15': ['glslify'], 7 | '17': ['glslify'], 8 | '19': ['brfs'], 9 | '21': ['glslify'], 10 | '23': ['brfs', 'glslify'], 11 | '26': ['glslify'], 12 | '27': ['glslify'], 13 | '28': ['glslify'], 14 | '29': ['glslify'], 15 | '30': ['glslify'], 16 | 'grid': ['hbsfy', 'brfs'] 17 | } 18 | -------------------------------------------------------------------------------- /dev.js: -------------------------------------------------------------------------------- 1 | const budo = require('budo') 2 | const argv = require('minimist')(process.argv.slice(2)) 3 | const path = require('path') 4 | const babelify = require('babelify') 5 | const open = require('opn') 6 | const fs = require('fs') 7 | const simpleHtml = require('simple-html-index') 8 | 9 | var entry = argv._[0] 10 | if (!entry) { 11 | entry = 'grid' 12 | } 13 | 14 | const transforms = require('./config') 15 | 16 | const entryFilename = entry === 'grid' 17 | ? path.join(entry, 'index.js') 18 | : (entry + '.js') 19 | 20 | const entryFile = path.resolve(__dirname, 'src', entryFilename) 21 | budo(entryFile, { 22 | serve: 'static/' + entry + '.js', 23 | live: true, 24 | verbose: true, 25 | dir: __dirname, 26 | stream: process.stdout, 27 | forceDefaultIndex: true, 28 | defaultIndex: function (opt) { 29 | var html = entry === 'grid' ? 'index.html' : entry + '.html' 30 | if (!fs.existsSync(html)) return simpleHtml(opt) 31 | return fs.createReadStream(html) 32 | }, 33 | browserify: { 34 | debug: false, 35 | transform: [ 36 | babelify.configure({ presets: ['es2015'] }), 37 | [ 'installify', { save: true } ] 38 | ].concat(transforms[entry] || []) 39 | } 40 | }).on('connect', function (ev) { 41 | if (argv.open) open(ev.uri) 42 | }) 43 | -------------------------------------------------------------------------------- /ideas/redux.md: -------------------------------------------------------------------------------- 1 | explore how redux could be used in WebGL/Canvas apps 2 | 3 | e.g. A painting app 4 | 5 | Triggers for Undo/Redo: 6 | - brush stroke 7 | - 8 | 9 | Actions w/o Undo/Redo: 10 | - hand/zoom tool 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | codevember 7 | 8 | 9 | 17 | 18 | 19 |
20 |
#codevember
21 |
22 |
one creative demo per day for the month of November, 2015
23 |
(desktop recommended)
24 |
25 |
my twitter source code
26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/1.js: -------------------------------------------------------------------------------- 1 | const canvas = document.querySelector('canvas') 2 | const ctx = canvas.getContext('2d') 3 | 4 | const app = require('canvas-loop')(ctx.canvas, { 5 | scale: Math.min(2, window.devicePixelRatio), 6 | parent: () => [ window.innerWidth, window.innerHeight ] 7 | }).start() 8 | 9 | const loadSvg = require('load-svg') 10 | const extractSvg = require('extract-svg-path').parse 11 | 12 | const svgMesh = require('svg-mesh-3d') 13 | const drawTriangles = require('draw-triangles-2d') 14 | const createPoint = require('verlet-point') 15 | const createConstraint = require('verlet-constraint') 16 | const reindex = require('mesh-reindex') 17 | const unindex = require('unindex-mesh') 18 | const random = require('random-float') 19 | const array = require('new-array') 20 | 21 | const lowEnd = require('./is-low-end')() 22 | 23 | const randomVector = (scale = 1) => { 24 | return array(3).map(() => random(-scale, scale)) 25 | } 26 | 27 | const world = require('verlet-system')({ 28 | gravity: [0, 0], 29 | min: [null, 0], 30 | max: [null, 0] 31 | }) 32 | 33 | loadSvg('assets/feather.svg', (err, svg) => { 34 | if (err) return alert(err.message) 35 | start(extractSvg(svg)) 36 | }) 37 | 38 | function start (icon) { 39 | let mesh = svgMesh(icon, { 40 | randomization: lowEnd ? 100 : 500, 41 | simplify: 0.01, 42 | scale: 10 43 | }) 44 | 45 | mesh = reindex(unindex(mesh.positions, mesh.cells)) 46 | 47 | mesh.positions = mesh.positions.map(x => { 48 | const size = 125 49 | return [ 50 | x[0] * size + window.innerWidth / 2, 51 | -x[1] * size + window.innerHeight / 2 ] 52 | }) 53 | 54 | world.points = mesh.positions.map(pos => { 55 | return createPoint({ position: pos }) 56 | }) 57 | 58 | const constraints = mesh.cells.map(cell => { 59 | const points = cell.map(i => world.points[i]) 60 | 61 | // adjust mass 62 | points.forEach(pt => { 63 | pt.mass = random(0.65, 1.0) 64 | }) 65 | 66 | const opt = { stiffness: 1 } 67 | const tri = [ 68 | createConstraint([ points[0], points[1]], opt), 69 | createConstraint([ points[1], points[2]], opt), 70 | createConstraint([ points[2], points[0]], opt) 71 | ] 72 | return tri 73 | }).reduce((a, b) => a.concat(b), []) 74 | 75 | const onClick = (ev) => { 76 | ev.preventDefault() 77 | world.gravity = [0, 600] 78 | world.points.forEach(point => { 79 | point.addForce(randomVector(5)) 80 | }) 81 | } 82 | 83 | canvas.addEventListener('click', onClick) 84 | canvas.addEventListener('touchstart', onClick) 85 | 86 | app.on('tick', (dt) => { 87 | dt = Math.min(dt, 30) 88 | 89 | const [width, height] = app.shape 90 | ctx.save() 91 | ctx.scale(app.scale, app.scale) 92 | ctx.clearRect(0, 0, width, height) 93 | 94 | const grad = ctx.createLinearGradient(-width * 0.5, 0, width * 1.5, 0) 95 | grad.addColorStop(0, '#e356c0') 96 | grad.addColorStop(1, '#e77b23') 97 | ctx.fillStyle = grad 98 | ctx.fillRect(0, 0, width, height) 99 | 100 | ctx.beginPath() 101 | world.max[1] = height 102 | constraints.forEach(c => c.solve()) 103 | world.integrate(world.points, dt / 1000) 104 | 105 | const positions = world.points.map(x => x.position) 106 | drawTriangles(ctx, positions, mesh.cells) 107 | ctx.fillStyle = '#fff' 108 | ctx.fill() 109 | ctx.restore() 110 | }) 111 | } 112 | -------------------------------------------------------------------------------- /src/14.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | import createView from './three-orbit-app' 3 | import createErrorPage from './fatal-error' 4 | import isMobile from './is-mobile' 5 | import injectDefines from 'glsl-inject-defines' 6 | 7 | const error = createErrorPage() 8 | const mobile = isMobile() 9 | const glslify = require('glslify') 10 | const EffectComposer = require('three-effectcomposer')(THREE) 11 | const fxaa = require('three-shader-fxaa')(THREE) 12 | const PostShader = require('./shaders/14-post') 13 | 14 | const app = createView({ 15 | distance: 7, 16 | scale: mobile ? 1 : undefined, 17 | position: [0, 0, 4], 18 | antialias: false, 19 | alpha: false 20 | }) 21 | 22 | app.renderer.setClearColor(mobile ? 0x252525 : 0x121212, 1) 23 | 24 | const geometries = [ 25 | new THREE.TorusKnotGeometry(1, 0.85, 4, 5, 5, 24), 26 | new THREE.TorusKnotGeometry(1, 0.5, 64, 32, 6, 24), 27 | new THREE.TorusKnotGeometry(1, 0.85, 24, 5, 16, 24), 28 | new THREE.TorusKnotGeometry(1, 0.5, 64, 32, 14, 24), 29 | new THREE.TorusKnotGeometry(1, 0.4, 64, 20, 4, 24), 30 | new THREE.TorusKnotGeometry(1, 0.2, 50, 14, 3, 24) 31 | ] 32 | 33 | let ptr = mobile ? 1 : 0 34 | const fragShader = glslify(__dirname + '/shaders/14.frag') 35 | const defines = {} 36 | if (!mobile) defines.DESKTOP = true 37 | 38 | const mat = new THREE.RawShaderMaterial({ 39 | color: 0xffffff, 40 | transparent: true, 41 | side: THREE.DoubleSide, 42 | uniforms: { 43 | color: { type: 'c', value: new THREE.Color() }, 44 | opacity: { type: 'f', value: 1 }, 45 | mode: { type: 'i', value: 0 }, 46 | iGlobalTime: { type: 'f', value: 0 } 47 | }, 48 | fragmentShader: injectDefines(fragShader, defines), 49 | vertexShader: glslify(__dirname + '/shaders/14.vert') 50 | }) 51 | 52 | const meshes = geometries.map(x => new THREE.Mesh(x, mat)) 53 | meshes.forEach((mesh, i) => { 54 | mesh.visible = i === ptr 55 | app.scene.add(mesh) 56 | }) 57 | 58 | app.canvas.addEventListener('click', onClick) 59 | app.canvas.addEventListener('touchstart', onClick) 60 | 61 | function onClick (ev) { 62 | ev.preventDefault() 63 | meshes[ptr].visible = false 64 | ptr = (ptr + 1) % meshes.length 65 | meshes[ptr].visible = true 66 | mat.uniforms.mode.value = Math.round(Math.random()) 67 | } 68 | 69 | let effects = [] 70 | let time = 0 71 | 72 | if (!mobile) { 73 | setupPost() 74 | } 75 | 76 | app.on('tick', (dt) => { 77 | time += dt / 1000 78 | mat.uniforms.iGlobalTime.value = time 79 | 80 | effects.forEach(effect => { 81 | if (effect.uniforms.resolution) effect.uniforms.resolution.value.set(app.shape[0], app.shape[1]) 82 | if (effect.uniforms.iGlobalTime) effect.uniforms.iGlobalTime.value = time 83 | }) 84 | }) 85 | 86 | function setupPost () { 87 | app.stop() 88 | 89 | const composer = new EffectComposer(app.renderer) 90 | app.composer = composer 91 | 92 | composer.addPass(new EffectComposer.RenderPass(app.scene, app.camera)) 93 | 94 | const fxaaPass = new EffectComposer.ShaderPass(fxaa()) 95 | effects.push(fxaaPass) 96 | composer.addPass(fxaaPass) 97 | 98 | const post = new EffectComposer.ShaderPass(PostShader) 99 | effects.push(post) 100 | post.renderToScreen = true 101 | composer.addPass(post) 102 | 103 | THREE.ImageUtils.loadTexture('assets/14-lut.png', undefined, (tex) => { 104 | tex.minFilter = tex.magFilter = THREE.LinearFilter 105 | effects.forEach(effect => { 106 | if (effect.uniforms.tLookup) { 107 | effect.uniforms.tLookup.value = tex 108 | } 109 | }) 110 | app.start() 111 | }, error) 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/15.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | import createView from './three-orbit-app' 3 | import desktopOnly from './desktop-only' 4 | import isMobile from './is-mobile' 5 | import unindex from 'unindex-mesh' 6 | import reindex from 'mesh-reindex' 7 | import quantize from 'quantize-vertices' 8 | import rescale from 'rescale-vertices' 9 | import getBounds from 'vertices-bounding-box' 10 | import randomSphere from 'gl-vec3/random' 11 | import parallel from 'run-parallel' 12 | import meshData from 'snowden/lo' 13 | 14 | const createComplex = require('three-simplicial-complex')(THREE) 15 | const error = require('./fatal-error')() 16 | const glslify = require('glslify') 17 | const EffectComposer = require('three-effectcomposer')(THREE) 18 | const PostShader = require('./shaders/15-post') 19 | 20 | if (isMobile()) { 21 | desktopOnly() 22 | // const element = error(` 23 | //
This demo may not perform well on mobile. Click 24 | // to continue. 25 | //
`) 26 | // const cont = document.querySelector('.continue') 27 | // console.log(element) 28 | 29 | // // cont.addEventListener('click', (ev) => { 30 | // // // element.parentNode.removeChild(element) 31 | // // // load() 32 | // // }) 33 | } else { 34 | load() 35 | } 36 | 37 | function load () { 38 | parallel([ 39 | next => loadTexture('assets/15-lut.png', next), 40 | next => loadTexture('assets/dust.jpg', next) 41 | ], (err, [ lut, dust ]) => { 42 | if (err) { 43 | return error(err) 44 | } 45 | start3D(meshData, lut, dust) 46 | }) 47 | } 48 | 49 | function start3D (meshData, lookup, dust) { 50 | let effects = [] 51 | let time = 0 52 | 53 | const app = createView({ 54 | distance: 7, 55 | antialias: false, 56 | scale: 1, 57 | distanceBounds: [1, 9], 58 | position: [0, 0, 4] 59 | }) 60 | 61 | app.renderer.setClearColor('#181818', 1) 62 | 63 | let complex = reindex(unindex(meshData.positions, meshData.cells)) 64 | const bbox = getBounds(complex.positions) 65 | complex.positions = quantize(complex.positions, 5) 66 | complex.positions = rescale(complex.positions, bbox) 67 | const geom = createComplex(complex) 68 | 69 | const attribs = [] 70 | complex.cells.forEach(() => { 71 | const dir = new THREE.Vector3().fromArray(randomSphere([])) 72 | attribs.push(dir, dir, dir) 73 | }) 74 | 75 | const mat = new THREE.RawShaderMaterial({ 76 | transparent: true, 77 | side: THREE.DoubleSide, 78 | attributes: { 79 | direction: { type: 'v3', value: attribs } 80 | }, 81 | uniforms: { 82 | mouse: { type: 'v3', value: new THREE.Vector3() }, 83 | color: { type: 'c', value: new THREE.Color() }, 84 | opacity: { type: 'f', value: 0.01 }, 85 | mode: { type: 'i', value: 0 }, 86 | iGlobalTime: { type: 'f', value: 0 } 87 | }, 88 | depthTest: false, 89 | depthWrite: false, 90 | wireframe: true, 91 | blending: THREE.AdditiveBlending, 92 | fragmentShader: glslify(__dirname + '/shaders/15.frag'), 93 | vertexShader: glslify(__dirname + '/shaders/15.vert') 94 | }) 95 | 96 | const mesh = new THREE.Mesh(geom, mat) 97 | app.scene.add(mesh) 98 | mesh.position.y = 0.25 99 | mesh.scale.multiplyScalar(0.35) 100 | 101 | setupPost() 102 | app.on('tick', (dt) => { 103 | time += dt / 1000 104 | mat.uniforms.iGlobalTime.value = time 105 | effects.forEach(effect => { 106 | if (effect.uniforms.resolution) effect.uniforms.resolution.value.set(app.shape[0], app.shape[1]) 107 | if (effect.uniforms.iGlobalTime) effect.uniforms.iGlobalTime.value = time 108 | if (effect.uniforms.theta) effect.uniforms.theta.value = app.controls.theta 109 | }) 110 | }) 111 | 112 | function setupPost () { 113 | const composer = new EffectComposer(app.renderer) 114 | app.composer = composer 115 | 116 | composer.addPass(new EffectComposer.RenderPass(app.scene, app.camera)) 117 | 118 | const post = new EffectComposer.ShaderPass(PostShader) 119 | effects.push(post) 120 | post.renderToScreen = true 121 | composer.addPass(post) 122 | 123 | effects.forEach(effect => { 124 | if (effect.uniforms.tLookup) effect.uniforms.tLookup.value = lookup 125 | if (effect.uniforms.tDust) effect.uniforms.tDust.value = dust 126 | if (effect.uniforms.dustResolution) { 127 | effect.uniforms.dustResolution.value.set(dust.image.width, dust.image.height) 128 | } 129 | }) 130 | } 131 | } 132 | 133 | function loadTexture (path, cb) { 134 | cb = cb || noop 135 | THREE.ImageUtils.loadTexture(path, undefined, (tex) => { 136 | tex.minFilter = tex.magFilter = THREE.LinearFilter 137 | cb(null, tex) 138 | }, () => { 139 | cb(new Error('could not load ' + path)) 140 | }) 141 | } 142 | -------------------------------------------------------------------------------- /src/16.js: -------------------------------------------------------------------------------- 1 | import audioDemo from './util/audio-demo-2d' 2 | import { getAnalyserAverages } from './audio-util' 3 | import bezierCurve from 'bezier-curve' 4 | 5 | const curve = [ 6 | [-0.9, 0], 7 | [-0.5, -0.5], 8 | [0.5, -0.5], 9 | [0.9, 0] 10 | ] 11 | const controls = curve.slice(1).reduce((a, b) => a.concat(b), []) 12 | 13 | const tracks = [ 14 | 'https://soundcloud.com/roman-mars/99-invisible-161-show-of-force', 15 | 'https://soundcloud.com/roman-mars/184-rajneeshpuram', 16 | 'https://soundcloud.com/roman-mars/99-invisible-110-structural-integrity', 17 | 'https://soundcloud.com/roman-mars/99-invisible-167-voices-in-the-wire', 18 | 'https://soundcloud.com/roman-mars/99-invisible-162-mystery-house' 19 | ] 20 | 21 | const track = tracks[Math.floor(Math.random() * tracks.length)] 22 | const demo = audioDemo(track) 23 | 24 | demo.on('render', (context, analyser) => { 25 | const [ width, height ] = demo.shape 26 | 27 | // male speech - 85-180 Hz 28 | const avg = getAnalyserAverages(analyser, 85, 180) 29 | context.translate(width / 2, height / 2) 30 | 31 | const radius = Math.min(200, Math.min(width, height) / 2) 32 | context.scale(radius, radius) 33 | 34 | const point = bezierCurve(avg, curve) 35 | context.beginPath() 36 | context.lineWidth = 3 / radius 37 | context.lineCap = 'round' 38 | context.strokeStyle = '#ca211d' 39 | context.moveTo(0, 0.35) 40 | context.lineTo(...point) 41 | context.stroke() 42 | 43 | context.lineWidth = 5 / radius 44 | context.beginPath() 45 | context.moveTo(...curve[0]) 46 | context.bezierCurveTo(...controls) 47 | context.strokeStyle = '#000' 48 | context.stroke() 49 | }) 50 | -------------------------------------------------------------------------------- /src/17.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | 3 | import createWorld from 'verlet-system/3d' 4 | import createConstraint from 'verlet-constraint/3d' 5 | import createPoint from 'verlet-point/3d' 6 | import createApp from './three-orbit-app' 7 | import randomSphere from 'gl-vec3/random' 8 | import subtract from 'gl-vec3/subtract' 9 | import normalize from 'gl-vec3/normalize' 10 | import scale3 from 'gl-vec3/scale' 11 | import lerp from 'gl-vec3/lerp' 12 | import newArray from 'new-array' 13 | import flattenVertices from 'flatten-vertex-data' 14 | import random from 'random-float' 15 | 16 | const glslify = require('glslify') 17 | const flattenArray = (a, b) => a.concat(b) 18 | 19 | const app = createApp({ 20 | antialias: true, 21 | scale: window.devicePixelRatio 22 | }) 23 | app.renderer.setClearColor('#3d0e3b', 1) 24 | 25 | const radius = 0.75 26 | const segments = 20 27 | const capacity = 100 28 | const origin = [ 0, 0, 0 ] 29 | 30 | const world = createWorld() 31 | const groups = newArray(capacity).map(() => { 32 | let start = origin 33 | let end = randomSphere([], radius) 34 | return newArray(segments).map((x, i) => { 35 | const curStep = i % 2 === 0 ? i : (i + 1) 36 | const t = curStep / (segments - 1) 37 | const pt = createPoint({ 38 | position: lerp([], start, end, t), 39 | mass: random(0.8, 1.0) 40 | }) 41 | return pt 42 | }) 43 | }) 44 | 45 | const points = groups.reduce(flattenArray, []) 46 | 47 | const constraints = groups.map(group => { 48 | const opt = { stiffness: 1 } 49 | const cGroup = [] 50 | for (let i = group.length - 2; i >= 0; i--) { 51 | const c = createConstraint([ group[i], group[i + 1] ], opt) 52 | cGroup.push(c) 53 | } 54 | return cGroup 55 | }).reduce(flattenArray, []) 56 | 57 | const geometry = new THREE.BufferGeometry() 58 | const vertices = new Float32Array(capacity * segments * 3) 59 | const attrib = new THREE.BufferAttribute(vertices, 3) 60 | geometry.addAttribute('position', attrib) 61 | updatePoints() 62 | 63 | const material = new THREE.RawShaderMaterial({ 64 | vertexShader: glslify(__dirname + '/shaders/17.vert'), 65 | fragmentShader: glslify(__dirname + '/shaders/17.frag'), 66 | uniforms: { 67 | color: { type: 'c', value: new THREE.Color('#5be72b') }, 68 | opacity: { type: 'f', value: 0.95 }, 69 | radius: { type: 'f', value: radius } 70 | }, 71 | wireframe: true, 72 | wireframeLinewidth: 1.25, 73 | depthTest: false, 74 | depthWrite: false, 75 | blending: THREE.AdditiveBlending, 76 | transparent: true 77 | }) 78 | 79 | // Three r69 doesn't set this... 80 | app.renderer.getContext().lineWidth(material.wireframeLinewidth) 81 | 82 | const mesh = new THREE.Mesh(geometry, material) 83 | app.scene.add(mesh) 84 | 85 | window.addEventListener('click', forcePush) 86 | window.addEventListener('touchend', forcePush) 87 | forcePush() 88 | 89 | app.on('tick', dt => { 90 | dt = Math.min(dt, 30) / 1000 91 | constraints.forEach(c => c.solve()) 92 | 93 | // pin center 94 | groups.forEach(group => { 95 | group[0].place(origin) 96 | }) 97 | 98 | world.integrate(points, dt) 99 | updatePoints() 100 | 101 | mesh.rotation.y += dt * 0.25 102 | }) 103 | 104 | function updatePoints () { 105 | const positions = points.map(x => [ 106 | x.position[0], 107 | x.position[1], 108 | x.position[2] 109 | ]) 110 | flattenVertices(positions, vertices) 111 | attrib.needsUpdate = true 112 | } 113 | 114 | function forcePush () { 115 | groups.forEach(group => { 116 | const p1 = group[group.length - 1] 117 | const p2pos = randomSphere([], Math.random() * radius) 118 | const force = subtract([], p2pos, p1.position) 119 | normalize(force, force) 120 | for (let i = group.length - 2; i >= 0; i--) { 121 | group[i].addForce(scale3([], force, 0.009)) 122 | } 123 | }) 124 | } 125 | -------------------------------------------------------------------------------- /src/19.js: -------------------------------------------------------------------------------- 1 | /* 2 | The code for this demo visualizes itself. 3 | 4 | Change it to see the AST update 5 | the visualization. 6 | */ 7 | 8 | // Include some dependencies 9 | const CodeMirror = require('codemirror/lib/codemirror') 10 | const fs = require('fs') 11 | const insert = require('insert-css') 12 | const acorn = require('acorn') 13 | const walk = require('acorn/dist/walk') 14 | const createLoop = require('canvas-loop') 15 | const colorHash = new (require('color-hash'))() 16 | const astTypes = require('./util/acorn-types') 17 | 18 | // Include CodeMirror JavaScript handling 19 | require('codemirror/mode/javascript/javascript') 20 | 21 | // CSS styles 22 | insert(fs.readFileSync(require.resolve('codemirror/lib/codemirror.css'), 'utf8')) 23 | insert(fs.readFileSync(require.resolve('codemirror/theme/material.css'), 'utf8')) 24 | 25 | // The source code for this file 26 | const src = fs.readFileSync(__filename, 'utf8') 27 | 28 | // CodeMirror setup function 29 | const textArea = document.querySelector('#text') 30 | function createEditor (callback) { 31 | const editor = CodeMirror(textArea, { 32 | lineNumbers: true, 33 | styleActiveLine: true, 34 | matchBrackets: true, 35 | viewportMargin: Infinity, 36 | theme: 'material', 37 | value: src, 38 | mode: 'javascript' 39 | }) 40 | 41 | resize() 42 | window.addEventListener('resize', resize) 43 | 44 | function resize () { 45 | editor.setSize(window.innerWidth, window.innerHeight) 46 | } 47 | 48 | const changed = () => { 49 | callback(editor.getValue()) 50 | } 51 | editor.on('change', changed) 52 | process.nextTick(changed) 53 | return editor 54 | } 55 | 56 | // Parse the AST on text edit 57 | let previousError = null 58 | const editor = createEditor(text => { 59 | try { 60 | const ast = acorn.parse(text, { 61 | ecmaVersion: 6, 62 | sourceType: 'module', 63 | allowReserved: true, 64 | allowReturnOutsideFunction: true, 65 | allowHashBang: true 66 | }) 67 | 68 | clearPreviousError() 69 | update(ast, text) 70 | } catch (err) { 71 | if (err instanceof SyntaxError && err.loc) { 72 | clearPreviousError() 73 | previousError = err.loc.line - 1 74 | console.error(err) 75 | editor.addLineClass(previousError, 'background', 'line-error') 76 | } else { 77 | throw err 78 | } 79 | } 80 | }) 81 | 82 | // Our full-screen 83 | const canvas = document.createElement('canvas') 84 | document.body.insertBefore(canvas, textArea) 85 | 86 | // Bootstrap window resizing and canvas scaling 87 | let nodes = [] 88 | const ctx = canvas.getContext('2d') 89 | const app = createLoop(canvas, { 90 | scale: window.devicePixelRatio 91 | }) 92 | app.on('resize', render) 93 | 94 | // Remove the last error line 95 | function clearPreviousError () { 96 | if (typeof previousError !== 'number') return 97 | editor.removeLineClass(previousError, 'background', 'line-error') 98 | } 99 | 100 | // Walk the AST to get all nodes 101 | function update (ast, text) { 102 | nodes = allNodes(ast) 103 | render() 104 | } 105 | 106 | // Render each node with its own styling 107 | function render () { 108 | const [ width, height ] = app.shape 109 | ctx.save() 110 | ctx.scale(app.scale, app.scale) 111 | ctx.clearRect(0, 0, width, height) 112 | 113 | ctx.fillStyle = 'white' 114 | ctx.translate(width / 2, height / 2) 115 | 116 | const length = editor.getValue().length 117 | nodes.forEach((n, i) => { 118 | const { start, end, type } = n 119 | const size = (end - start) / length 120 | const radius = size * 50 121 | 122 | ctx.strokeStyle = colorHash.hex(type) 123 | const angle = (start / length) * Math.PI * 2 124 | const parent = Math.min(width, height) / 2 125 | const x0 = Math.cos(angle) * parent / 2 126 | const y0 = Math.sin(angle) * parent / 2 127 | const x1 = Math.cos(angle) * (parent + parent * radius) 128 | const y1 = Math.sin(angle) * (parent + parent * radius) 129 | ctx.beginPath() 130 | ctx.moveTo(x0, y0) 131 | ctx.lineTo(x1, y1) 132 | ctx.stroke() 133 | }) 134 | 135 | ctx.restore() 136 | } 137 | 138 | // Walks all nodes and returns them as an array 139 | function allNodes (ast) { 140 | const nodes = [] 141 | walkAll(ast, node => nodes.push(node)) 142 | return nodes 143 | } 144 | 145 | // Walk all nodes with a visitor function 146 | function walkAll (ast, callback) { 147 | const visitors = astTypes.reduce((dict, type) => { 148 | dict[type] = callback 149 | return dict 150 | }, {}) 151 | walk.simple(ast, visitors) 152 | } 153 | -------------------------------------------------------------------------------- /src/20.js: -------------------------------------------------------------------------------- 1 | import loadImage from 'img' 2 | import createErrorPage from './fatal-error' 3 | import SimplexNoise from 'simplex-noise' 4 | import randomInt from 'random-int' 5 | import random from 'random-float' 6 | 7 | const simplex = new SimplexNoise() 8 | const error = createErrorPage() 9 | const canvas = document.createElement('canvas') 10 | document.body.appendChild(canvas) 11 | document.body.style.margin = '0' 12 | canvas.style.position = 'absolute' 13 | 14 | const ctx = canvas.getContext('2d') 15 | 16 | loadImage('assets/paper1-diffuse.jpg', (err, img) => { 17 | if (err) return error(err) 18 | const [width, height] = [ 450, 450 ] 19 | const scale = window.devicePixelRatio 20 | const pattern = ctx.createPattern(img, 'repeat') 21 | canvas.width = width * scale 22 | canvas.height = height * scale 23 | canvas.style.width = width + 'px' 24 | canvas.style.height = height + 'px' 25 | 26 | render() 27 | resize() 28 | window.addEventListener('touchend', render) 29 | window.addEventListener('click', render) 30 | window.addEventListener('resize', resize) 31 | 32 | function resize () { 33 | canvas.style.left = ((window.innerWidth - width) / 2) + 'px' 34 | canvas.style.top = ((window.innerHeight - height) / 2) + 'px' 35 | } 36 | 37 | function render () { 38 | ctx.save() 39 | ctx.scale(scale, scale) 40 | ctx.clearRect(0, 0, width, height) 41 | ctx.fillStyle = pattern 42 | ctx.fillRect(0, 0, width, height) 43 | ctx.fillStyle = 'black' 44 | draw() 45 | ctx.restore() 46 | } 47 | 48 | function draw () { 49 | const blockCount = randomInt(10, 40) 50 | const blockSize = width / blockCount 51 | const cols = width / blockSize 52 | const count = blockCount * (height + blockCount * 2) 53 | const rows = Math.ceil(count / cols) 54 | const half = blockSize / 2 55 | ctx.beginPath() 56 | ctx.translate(half, half) 57 | for (let i = 0; i < (rows * cols); i++) { 58 | const x = Math.floor(i % cols) * blockSize 59 | const y = Math.floor(i / cols) * blockSize 60 | let s = random(0.01, 0.05) 61 | let n = simplex.noise3D(x * s, y * s, Date.now() / 1000) 62 | if (x === 0) ctx.moveTo(x, y) 63 | else ctx.lineTo(x, y) 64 | ctx.lineTo(x, y + blockSize / 2 * n) 65 | } 66 | ctx.lineWidth = 1.5 67 | ctx.lineJoin = 'round' 68 | ctx.globalAlpha = 0.95 69 | ctx.stroke() 70 | ctx.translate(-half, -half) 71 | } 72 | }) 73 | -------------------------------------------------------------------------------- /src/22.js: -------------------------------------------------------------------------------- 1 | const punch = require('./util/punch-card') 2 | const sunTzu = require('sun-tzu-quotes') 3 | 4 | const canvas = document.querySelector('canvas') 5 | const ctx = canvas.getContext('2d') 6 | 7 | const CARD_WIDTH = 0.7 8 | const CARD_HEIGHT = 1.0 9 | const punchColumns = 14 10 | const punchRows = 8 11 | 12 | let text 13 | canvas.addEventListener('click', next) 14 | canvas.addEventListener('touchend', next) 15 | window.addEventListener('resize', renderCard) 16 | next() 17 | 18 | function next () { 19 | text = sunTzu() 20 | document.querySelector('#quote').innerText = text 21 | renderCard() 22 | } 23 | 24 | function renderCard () { 25 | const size = 70 26 | const spacing = 0.05 27 | const words = getCardChunks(text) 28 | const width = window.innerWidth 29 | 30 | const count = words.length 31 | const computedWidth = (width + spacing * size) 32 | const cols = Math.floor(computedWidth / ((CARD_WIDTH + spacing) * size)) 33 | const rows = Math.ceil(count / cols) 34 | const height = rows * (CARD_HEIGHT * size) + (rows - 1) * (spacing * size) 35 | 36 | const scale = window.devicePixelRatio 37 | canvas.width = width * scale 38 | canvas.height = height * scale 39 | canvas.style.width = '100%' 40 | canvas.style.height = 'auto' 41 | 42 | ctx.save() 43 | ctx.scale(scale, scale) 44 | ctx.clearRect(0, 0, width, height) 45 | ctx.scale(size, size) 46 | ctx.translate(CARD_WIDTH / 2, CARD_HEIGHT / 2) 47 | words.forEach((word, i) => { 48 | const x1 = Math.floor(i % cols) 49 | const y1 = Math.floor(i / cols) 50 | const x = x1 * CARD_WIDTH + x1 * spacing 51 | const y = y1 * CARD_HEIGHT + y1 * spacing 52 | ctx.translate(x, y) 53 | ctx.beginPath() 54 | renderCardBackground() 55 | ctx.fillStyle = '#CEB274' 56 | ctx.fill() 57 | ctx.beginPath() 58 | renderCardPunches(punch(word)) 59 | ctx.fillStyle = '#5a513c' 60 | ctx.fill() 61 | ctx.translate(-x, -y) 62 | }) 63 | ctx.restore() 64 | } 65 | 66 | function renderCardBackground () { 67 | const [ width, height ] = [ CARD_WIDTH, CARD_HEIGHT ] 68 | const [ x, y ] = [ -width / 2, -height / 2 ] 69 | const edge = 0.10 70 | ctx.moveTo(x, y) 71 | ctx.lineTo(x + width - edge, y) 72 | ctx.lineTo(x + width, y + edge) 73 | ctx.lineTo(x + width, y + height) 74 | ctx.lineTo(x, y + height) 75 | ctx.lineTo(x, y) 76 | } 77 | 78 | function renderCardPunches (rows) { 79 | const spacing = 0.02 80 | const header = 0.12 81 | const [ x, y ] = [ -CARD_WIDTH / 2, -CARD_HEIGHT / 2 + header ] 82 | 83 | const cellWidth = (CARD_WIDTH + spacing) / punchColumns - spacing 84 | const cellHeight = 0.75 * ((CARD_HEIGHT + spacing) / punchRows - spacing) 85 | 86 | rows.forEach((row, rowIndex) => { 87 | row.forEach(colIndex => { 88 | ctx.rect( 89 | x + colIndex * cellWidth + colIndex * spacing, 90 | y + rowIndex * cellHeight + rowIndex * spacing, 91 | cellWidth, 92 | cellHeight) 93 | }) 94 | }) 95 | } 96 | 97 | function getCardChunks (text) { 98 | return text.split(/\s+/g) 99 | .map(x => x.replace(/[^A-Z1-9]+/ig, '')) 100 | .filter(Boolean) 101 | .map((x, i, list) => { 102 | const cap = i < list.length - 1 ? (punchRows - 1) : punchRows 103 | if (x.length > cap) { 104 | let str = x 105 | const chunks = [] 106 | while (str.length > 0) { 107 | const chunk = str.slice(0, cap) 108 | str = str.slice(cap) 109 | chunks.push(chunk) 110 | } 111 | if (i < list.length - 1 && chunks.length) { 112 | chunks[chunks.length - 1] += ' ' 113 | } 114 | return chunks 115 | } 116 | if (i < list.length - 1) { 117 | x = x + ' ' 118 | } 119 | return [ x ] 120 | }) 121 | .reduce((a, b) => a.concat(b), []) 122 | } 123 | -------------------------------------------------------------------------------- /src/23.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | const createOrbit = require('./three-orbit-app') 3 | const createText = require('three-bmfont-text') 4 | const loadBMFont = require('load-bmfont') 5 | const fatal = require('./fatal-error')() 6 | const parallel = require('run-parallel') 7 | const once = require('once') 8 | const glslify = require('glslify') 9 | const marvel = require('marvel-characters/characters.json') 10 | const shuffle = require('array-shuffle') 11 | 12 | parallel([ 13 | (next) => loadBMFont('assets/KelsonSans.fnt', next), 14 | (next) => { 15 | next = once(next) 16 | THREE.ImageUtils.loadTexture('assets/KelsonSans.png', undefined, 17 | (tex) => next(null, tex), 18 | (err) => next(err)) 19 | } 20 | ], (err, [font, texture]) => { 21 | if (err) return fatal(err) 22 | texture.minFilter = THREE.LinearFilter 23 | texture.magFilter = THREE.LinearFilter 24 | start(font, texture) 25 | }) 26 | 27 | function start (font, texture) { 28 | const app = createOrbit({ 29 | position: [0, 0, 1], 30 | near: 0.01, 31 | antialias: false, 32 | scale: Math.min(2, window.devicePixelRatio), 33 | distance: 60, 34 | far: 1000 35 | }) 36 | app.renderer.setClearColor(0xffffff, 1) 37 | 38 | const textOpts = { 39 | text: '', 40 | width: 500, 41 | letterSpacing: -5, 42 | lineHeight: 20, 43 | font: font, 44 | align: 'center', 45 | flipY: texture.flipY 46 | } 47 | const geom = createText(textOpts) 48 | 49 | const material = new THREE.RawShaderMaterial({ 50 | uniforms: { 51 | opacity: { type: 'f', value: 1 }, 52 | color: { type: 'c', value: new THREE.Color('#000') }, 53 | map: { type: 't', value: texture }, 54 | iGlobalTime: { type: 'f', value: 0 }, 55 | textSize: { type: 'v2', value: new THREE.Vector2() }, 56 | }, 57 | vertexShader: glslify(__dirname + '/shaders/23.vert'), 58 | fragmentShader: glslify(__dirname + '/shaders/23.frag'), 59 | map: texture, 60 | transparent: true, 61 | side: THREE.FrontSide, 62 | depthWrite: false, 63 | depthTest: false, 64 | color: 'rgb(230, 230, 230)' 65 | }) 66 | 67 | var text = new THREE.Mesh(geom, material) 68 | 69 | var textAnchor = new THREE.Object3D() 70 | textAnchor.add(text) 71 | app.scene.add(textAnchor) 72 | 73 | const scalar = -0.1 74 | textAnchor.scale.set(-scalar, scalar, scalar) 75 | 76 | let time = 0 77 | app.on('tick', function (dt) { 78 | time += dt / 1000 79 | material.uniforms.iGlobalTime.value = time 80 | }) 81 | 82 | updateText(shuffle(marvel).slice(0, 100).join(', ')) 83 | 84 | function updateText (copy) { 85 | textOpts.text = copy.toLowerCase() 86 | geom.update(textOpts) 87 | const layout = geom.layout 88 | text.position.set(-layout.width / 2, layout.height / 2, 0) 89 | console.log(layout.width, layout.height) 90 | material.uniforms.textSize.value.set(layout.width, layout.height) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/26.js: -------------------------------------------------------------------------------- 1 | window.THREE = require('three') 2 | window._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace } 3 | THREE.typeface_js = window._typeface_js 4 | 5 | const googleEquirect = require('google-panorama-equirectangular') 6 | const googlePano = require('google-panorama-by-location') 7 | const error = require('./fatal-error')() 8 | const simplex = new (require('simplex-noise'))() 9 | require('./gl/helvetiker.typeface')() 10 | const mouse = require('touch-position')() 11 | 12 | const app = require('./three-orbit-app')({ 13 | position: [0, 0, 0.00001], 14 | near: 0.01, 15 | far: 10000, 16 | fov: 56, 17 | zoom: false, 18 | alpha: false, 19 | rotateSpeed: 0.15, 20 | distanceBounds: [0, 10], 21 | distance: 0, 22 | pinch: false 23 | }) 24 | 25 | const cubeCamera = new THREE.CubeCamera(1, 1000, 256) 26 | app.scene.add(cubeCamera) 27 | 28 | const cubeTarget = cubeCamera.renderTarget 29 | const geometry = new THREE.SphereGeometry(1000, 32, 32) 30 | 31 | const mat4 = new THREE.Matrix4().makeRotationY(Math.PI / 2) 32 | mat4.scale(new THREE.Vector3(-1, 1, 1)) 33 | geometry.applyMatrix(mat4) 34 | 35 | const material = new THREE.MeshBasicMaterial({ 36 | map: new THREE.Texture(), 37 | side: THREE.DoubleSide 38 | }) 39 | 40 | const panosphere = new THREE.Mesh(geometry, material) 41 | app.scene.add(panosphere) 42 | 43 | const envMaterial = new THREE.MeshBasicMaterial({ 44 | side: THREE.DoubleSide, 45 | envMap: cubeTarget, 46 | reflectivity: 0.8, 47 | combine: THREE.MultiplyOperation 48 | }) 49 | 50 | let mesh, textGeo, textMesh 51 | app.on('tick', dt => { 52 | const x = 0.1 * ((mouse[0] / window.innerWidth) * 2 - 1) 53 | const y = 0.1 * ((mouse[1] / window.innerHeight) * 2 - 1) 54 | if (textMesh) textMesh.rotation.y = x 55 | if (textMesh) textMesh.rotation.x = y 56 | }) 57 | 58 | const list = [ 59 | { location: [40.490026, -75.07292000000001], heading: -Math.PI }, 60 | [0.203972, 37.45297400000004], 61 | { location: [30.185036, -84.724629], heading: Math.PI }, 62 | { location: [41.403286, 2.1746729999999843], heading: Math.PI / 2 }, 63 | [-26.9383, -68.74484799999999], 64 | { location: [-64.731934, -62.594516999999996], heading: Math.PI / 1.5 }, 65 | { location: [27.175515, 78.04145199999994], heading: -Math.PI / 5 } 66 | ] 67 | var uniqueRandomArray = require('unique-random-array') 68 | var random = uniqueRandomArray(list) 69 | 70 | newLocation(random()) 71 | 72 | function newLocation (item) { 73 | if (Array.isArray(item)) { 74 | item = { location: item, heading: 0 } 75 | } 76 | googlePano(item.location, (err, res) => { 77 | if (err) return error(err) 78 | const { id, tiles } = res 79 | panosphere.rotation.y = item.heading 80 | console.log([res.latitude, res.longitude]) 81 | googleEquirect(id, { 82 | zoom: 3, 83 | tiles, 84 | crossOrigin: 'Anonymous' 85 | }).on('complete', image => { 86 | material.map.image = image 87 | material.map.needsUpdate = true 88 | cubeCamera.updateCubeMap(app.renderer, app.scene) 89 | updateText(res.location.description) 90 | }) 91 | }) 92 | } 93 | 94 | function updateText (text) { 95 | if (mesh) { 96 | app.scene.remove(mesh) 97 | textGeo.dispose() 98 | } 99 | 100 | textGeo = new THREE.TextGeometry(text, { 101 | size: 64, 102 | height: 2, 103 | curveSegments: 2, 104 | font: 'helvetiker', 105 | weight: 'normal', 106 | style: 'normal', 107 | bevelThickness: 4, 108 | bevelSize: 1.5, 109 | bevelEnabled: true 110 | }) 111 | 112 | textGeo.computeBoundingBox() 113 | const mat4 = new THREE.Matrix4().makeTranslation( 114 | -(textGeo.boundingBox.max.x - textGeo.boundingBox.min.x) / 2, 115 | -(textGeo.boundingBox.max.y - textGeo.boundingBox.min.y) / 4, 116 | 0 117 | ) 118 | 119 | textGeo.applyMatrix(mat4) 120 | textGeo.computeFaceNormals() 121 | 122 | textMesh = new THREE.Mesh(textGeo, envMaterial) 123 | mesh = new THREE.Object3D() 124 | mesh.add(textMesh) 125 | app.scene.add(mesh) 126 | 127 | const heading = -Math.PI / 2 128 | const r = 650 129 | const x = Math.cos(heading) * r 130 | const z = Math.sin(heading) * r 131 | mesh.position.x = x 132 | mesh.position.z = z 133 | } 134 | -------------------------------------------------------------------------------- /src/27.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | 3 | const qs = require('query-string') 4 | const touches = require('touches') 5 | 6 | const query = qs.parse(window.location.search) 7 | const interactive = String(query.interactive) !== 'false' 8 | 9 | const Line = require('./gl/ThreeLine25D')(THREE) 10 | const glslify = require('glslify') 11 | 12 | const sphere = require('icosphere')(2) 13 | const triangulate = require('delaunay-triangulate') 14 | 15 | var app = require('./three-orbit-app')({ 16 | // rotate: false, 17 | position: [ 0, 0, -1 ], 18 | distance: 3, 19 | distanceBounds: [ 1, 100 ], 20 | antialias: true, 21 | zoom: interactive, 22 | rotate: interactive 23 | }) 24 | app.renderer.setClearColor('#dd6524', 1) 25 | document.querySelector('.loader').style.display = 'none' 26 | 27 | const cells = triangulate(sphere.positions) 28 | const path = cells.map(cell => { 29 | const [a, b, c] = cell 30 | return [ 31 | sphere.positions[a], 32 | sphere.positions[b], 33 | sphere.positions[c] 34 | ] 35 | }).reduce((a, b) => a.concat(b), []) 36 | 37 | // create our geometry 38 | const outerGeo = Line(sphere.positions) 39 | const innerGeo = Line(path) 40 | 41 | // create a material using a basic shader 42 | var material = createMaterial() 43 | material.uniforms.thickness.value = 0.2 44 | 45 | const outer = new THREE.Mesh(outerGeo, material) 46 | app.scene.add(outer) 47 | 48 | const material2 = createMaterial() 49 | material2.uniforms.opacity.value = 0.5 50 | material2.uniforms.thickness.value = 0.15 51 | material2.uniforms.color.value.setStyle('#6c180d') 52 | 53 | const inner = new THREE.Mesh(innerGeo, material2) 54 | inner.scale.multiplyScalar(0.75) 55 | app.scene.add(inner) 56 | 57 | let time = 0 58 | app.on('tick', dt => { 59 | dt = Math.min(dt, 30) 60 | dt /= 1000 61 | time += dt 62 | 63 | inner.rotation.y -= Math.sin(time * 0.001) * 0.001 64 | outer.rotation.y += dt * 0.1 65 | outer.rotation.x += dt * 0.05 66 | 67 | material.uniforms.iGlobalTime.value = time 68 | material2.uniforms.iGlobalTime.value = time 69 | }) 70 | 71 | if (!interactive) { 72 | app.once('tick', () => { 73 | app.stop() 74 | }) 75 | 76 | touches(window, { filtered: true }).on('start', ev => { 77 | app.start() 78 | }).on('end', ev => { 79 | app.stop() 80 | }) 81 | } 82 | 83 | function createMaterial () { 84 | return new THREE.RawShaderMaterial({ 85 | side: THREE.DoubleSide, 86 | transparent: true, 87 | uniforms: { 88 | aspect: { type: 'f', value: 1 }, 89 | thickness: { type: 'f', value: 0.01 }, 90 | opacity: { type: 'f', value: 1 }, 91 | iGlobalTime: { type: 'f', value: 0 }, 92 | color: { type: 'c', value: new THREE.Color('#fff') } 93 | }, 94 | attributes: { 95 | lineMiter: { type: 'f', value: 0 }, 96 | lineNormal: { type: 'v2', value: new THREE.Vector2() } 97 | }, 98 | vertexShader: glslify(__dirname + '/shaders/27.vert'), 99 | fragmentShader: glslify(__dirname + '/shaders/27.frag') 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /src/28.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | 3 | const createBackground = require('./gl/three-vignette-background') 4 | const googlePano = require('google-panorama-by-location') 5 | const googleEquirect = require('google-panorama-equirectangular') 6 | const qs = require('query-string') 7 | const error = require('./fatal-error')() 8 | const glslify = require('glslify') 9 | 10 | const app = require('./three-orbit-app')({ 11 | distance: 4 12 | }) 13 | 14 | document.querySelector('.loader').style.display = 'none' 15 | 16 | const potCanvas = document.createElement('canvas') 17 | potCanvas.width = 1024 18 | potCanvas.height = potCanvas.width / 2 19 | const potCtx = potCanvas.getContext('2d') 20 | 21 | const bg = createBackground() 22 | app.scene.add(bg) 23 | 24 | let time = 0 25 | app.on('tick', (dt) => { 26 | time += dt / 1000 27 | 28 | const [ width, height ] = app.shape 29 | bg.style({ 30 | aspect: width / height 31 | }) 32 | }) 33 | 34 | const smooth = qs.parse(window.location.search).smooth === 'true' 35 | const geometry = new THREE.TorusKnotGeometry(0.45, 0.25, 128, 256) 36 | const material = createMaterial(!smooth) 37 | 38 | const mesh = new THREE.Mesh(geometry, material) 39 | app.scene.add(mesh) 40 | 41 | const random = require('unique-random-array')([ 42 | [14.584186, 120.979963], 43 | [27.175515, 78.04145199999994], 44 | [10.642237, 122.23580400000003], 45 | [17.571335, 120.388777], 46 | [68.196537, 13.531821000000036], 47 | [0.203972, 37.45297400000004], 48 | [30.185036, -84.724629], 49 | [41.403286, 2.1746729999999843], 50 | [60.070408, 6.542394000000058] 51 | ]) 52 | 53 | mesh.visible = false 54 | setScene(random()) 55 | 56 | setInterval(function () { 57 | setScene(random()) 58 | }, 2000) 59 | 60 | function setScene (location) { 61 | googlePano(location, (err, res) => { 62 | if (err) return error(err) 63 | const { id, tiles } = res 64 | console.log([res.latitude, res.longitude]) 65 | googleEquirect(id, { 66 | zoom: 2, 67 | tiles, 68 | crossOrigin: 'Anonymous' 69 | }).on('complete', image => { 70 | potCtx.clearRect(0, 0, potCanvas.width, potCanvas.height) 71 | potCtx.drawImage(image, 0, 0, potCanvas.width, potCanvas.height) 72 | 73 | const material = mesh.material 74 | const tex = material.uniforms.map.value 75 | tex.generateMipmaps = true 76 | tex.minFilter = THREE.LinearMipMapLinearFilter 77 | tex.magFilter = THREE.LinearFilter 78 | tex.wrapS = tex.wrapT = THREE.RepeatWrapping 79 | tex.image = potCanvas 80 | tex.needsUpdate = true 81 | mesh.visible = true 82 | }) 83 | }) 84 | } 85 | 86 | function createMaterial (flat) { 87 | return new THREE.RawShaderMaterial({ 88 | uniforms: { 89 | map: { type: 't', value: new THREE.Texture() }, 90 | opacity: { type: 'f', value: 1 }, 91 | color: { type: 'c', value: new THREE.Color() } 92 | }, 93 | shading: flat ? THREE.FlatShading : THREE.SmoothShading, 94 | vertexShader: glslify(__dirname + '/shaders/28.vert'), 95 | fragmentShader: glslify(__dirname + '/shaders/28.frag') 96 | }) 97 | } -------------------------------------------------------------------------------- /src/29.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | 3 | const createBackground = require('./gl/three-vignette-background') 4 | const unindex = require('./gl/unindex-geometry') 5 | const error = require('./fatal-error')() 6 | const glslify = require('glslify') 7 | const threeHdrTexture = require('./gl/three-hdr-texture') 8 | const randomSphere = require('gl-vec3/random') 9 | const runParallel = require('run-parallel') 10 | const isMobile = require('./is-mobile')() 11 | 12 | const app = require('./three-orbit-app')({ 13 | distance: 5, 14 | distanceBounds: [ 3, 100 ] 15 | }) 16 | const ext = app.renderer.getContext().getExtension('OES_standard_derivatives') 17 | if (!ext) { 18 | error(`This device does not support OES_standard_derivatives`) 19 | } 20 | 21 | let material 22 | const bg = createBackground() 23 | app.scene.add(bg) 24 | 25 | updateBackground() 26 | 27 | runParallel([ 28 | (next) => threeHdrTexture('assets/apartment/Apartment_Reflection.hdr', next), 29 | (next) => threeHdrTexture('assets/apartment/Apartment_Diffuse.hdr', next), 30 | (next) => loadTexture('assets/burlap-normals.jpg', next) 31 | ], (err, textures) => { 32 | if (err) return error(err) 33 | 34 | const segs = isMobile ? 50 : 128 35 | let geometry = new THREE.SphereGeometry(1, segs, segs / 2) 36 | geometry = unindex(geometry) 37 | 38 | const randomDirections = geometry.faces.map(face => { 39 | const tmp = randomSphere([], 1) 40 | const center = new THREE.Vector3().fromArray(tmp) 41 | return [ 42 | center, center, center 43 | ] 44 | }).reduce((a, b) => a.concat(b), []) 45 | 46 | material = createMaterial({ 47 | randomDirection: { type: 'v3', value: randomDirections } 48 | }) 49 | 50 | const [ map, diffuseMap, normalMap ] = textures 51 | textures.forEach(setTextureParams) 52 | material.uniforms.map.value = map 53 | material.uniforms.diffuseMap.value = diffuseMap 54 | material.uniforms.normalMap.value = normalMap 55 | 56 | const mesh = new THREE.Mesh(geometry, material) 57 | app.scene.add(mesh) 58 | }) 59 | 60 | let time = 0 61 | app.on('tick', (dt) => { 62 | time += dt / 1000 63 | if (material) material.uniforms.iGlobalTime.value = time 64 | updateBackground() 65 | }) 66 | 67 | function updateBackground () { 68 | const [ width, height ] = app.shape 69 | bg.style({ 70 | aspect: width / height, 71 | grainScale: 1.5 / Math.min(width, height), 72 | colors: [ '#6d87a0', '#082b1b' ] 73 | }) 74 | } 75 | 76 | function setTextureParams (map) { 77 | map.generateMipmaps = false 78 | map.minFilter = THREE.LinearFilter 79 | map.magFilter = THREE.LinearFilter 80 | map.anisotropy = app.renderer.getMaxAnisotropy() 81 | map.wrapS = THREE.RepeatWrapping 82 | map.wrapT = THREE.RepeatWrapping 83 | } 84 | 85 | function createMaterial (attrib) { 86 | return new THREE.RawShaderMaterial({ 87 | attributes: attrib, 88 | uniforms: { 89 | map: { type: 't', value: new THREE.Texture() }, 90 | normalMap: { type: 't', value: new THREE.Texture() }, 91 | diffuseMap: { type: 't', value: new THREE.Texture() }, 92 | opacity: { type: 'f', value: 1 }, 93 | iGlobalTime: { type: 'f', value: 0 }, 94 | color: { type: 'c', value: new THREE.Color() } 95 | }, 96 | shading: THREE.SmoothShading, 97 | vertexShader: glslify(__dirname + '/shaders/29.vert'), 98 | fragmentShader: glslify(__dirname + '/shaders/29.frag') 99 | }) 100 | } 101 | 102 | function loadTexture (src, next) { 103 | THREE.ImageUtils.loadTexture(src, undefined, tex => { 104 | next(null, tex) 105 | }, () => { 106 | next(new Error(`could not load asset ${src}`)) 107 | }) 108 | } 109 | -------------------------------------------------------------------------------- /src/3.js: -------------------------------------------------------------------------------- 1 | const THREE = require('three') 2 | const createOrbitViewer = require('three-orbit-viewer')(THREE) 3 | const createAnalyser = require('web-audio-analyser') 4 | const lerp = require('lerp') 5 | const glslify = require('glslify') 6 | const newArray = require('new-array') 7 | import { frequencyAverages } from './audio-util' 8 | 9 | const AudioContext = window.AudioContext || window.webkitAudioContext 10 | 11 | // dumb mobile test 12 | const isMobile = /(iPad|iPhone|Android)/i.test(navigator.userAgent) 13 | const defaultSrc = 'https://api.soundcloud.com/tracks/198926710/stream?client_id=b95f61a90da961736c03f659c03cb0cc' 14 | 15 | require('soundcloud-badge')({ 16 | client_id: 'b95f61a90da961736c03f659c03cb0cc', 17 | song: 'https://soundcloud.com/else-official/else-zephyr', 18 | dark: true, 19 | getFonts: true 20 | }, function (err, src, data, div) { 21 | if (err) console.error(err) 22 | 23 | const app = createOrbitViewer({ 24 | clearColor: 0xffffff, 25 | clearAlpha: 1, 26 | fov: 60, 27 | contextAttributes: { 28 | antialias: false, 29 | alpha: false 30 | }, 31 | devicePixelRatio: Math.min(2, window.devicePixelRatio), 32 | position: new THREE.Vector3(0, 0, -2) 33 | }) 34 | app.controls.noRotate = true 35 | app.controls.noPan = true 36 | app.controls.noZoom = true 37 | 38 | var play = document.querySelector('.play') 39 | let audio = new Audio() 40 | audio.crossOrigin = 'Anonymous' 41 | audio.src = src || defaultSrc 42 | 43 | const geometry = new THREE.TorusGeometry(0.95, 0.15, 65, 200) 44 | const freqLow = newArray(geometry.vertices.length, 0.0) 45 | const freqMid = freqLow.slice() 46 | const freqHigh = freqLow.slice() 47 | 48 | const material = new THREE.ShaderMaterial({ 49 | vertexShader: glslify('./shaders/3.vert'), 50 | fragmentShader: glslify('./shaders/3.frag'), 51 | uniforms: { 52 | iGlobalTime: { type: 'f', value: 0 }, 53 | ring: { type: 'i', value: 0 }, 54 | color: { type: 'c', value: new THREE.Color() }, 55 | opacity: { type: 'f', value: 0.25 } 56 | }, 57 | // blending: THREE.AdditiveBlending, 58 | transparent: true, 59 | attributes: { 60 | freqLow: { type: 'f', value: freqLow }, 61 | freqMid: { type: 'f', value: freqMid }, 62 | freqHigh: { type: 'f', value: freqHigh } 63 | }, 64 | // depthTest: false, 65 | defines: { 66 | USE_MAP: '' 67 | }, 68 | wireframe: true 69 | }) 70 | 71 | const attribList = Object.keys(material.attributes).map(k => material.attributes[k]) 72 | 73 | const colors = [ 74 | '#000000', 75 | '#df18a4', 76 | '#188ddf' 77 | ] 78 | 79 | const rings = newArray(3).map((_, i) => { 80 | const mat = material.clone() 81 | mat.uniforms.ring.value = i 82 | mat.uniforms.color.value.set(colors[i % colors.length]) 83 | return new THREE.Mesh(geometry, mat) 84 | }) 85 | 86 | let analyser, sampleRate, fftSize 87 | if (audio && AudioContext) { 88 | analyser = createAnalyser(audio, { stereo: false }) 89 | const analyserNode = analyser.analyser 90 | sampleRate = analyser.ctx.sampleRate 91 | fftSize = analyserNode.fftSize 92 | } 93 | 94 | const getAverage = frequencyAverages(sampleRate, fftSize) 95 | 96 | if (!isMobile || !audio) { 97 | start() 98 | } else { 99 | play.style.display = 'flex' 100 | document.body.removeChild(play) 101 | document.body.appendChild(play) 102 | play.addEventListener('click', start, false) 103 | } 104 | 105 | app.on('tick', dt => { 106 | rings.forEach(ring => { 107 | ring.material.uniforms.iGlobalTime.value += dt / 1000 108 | const time = ring.material.uniforms.iGlobalTime.value * 0.5 109 | const off = 0.15 110 | ring.rotation.y = Math.sin(time) * off 111 | ring.rotation.x = Math.sin(Math.cos(time)) * off 112 | }) 113 | if (analyser) 114 | compute() 115 | }) 116 | 117 | function start () { 118 | document.body.removeChild(play) 119 | if (audio) audio.play() 120 | rings.forEach((ring, i, list) => { 121 | let a = lerp(0.5, 0.7, i / (list.length - 1)) 122 | if (isMobile) a *= 0.7 123 | ring.scale.set(a, a, a) 124 | app.scene.add(ring) 125 | }) 126 | } 127 | 128 | function compute () { 129 | const freqs = analyser.frequencies() 130 | const lowBass = getAverage(freqs, 0, 50) 131 | // const highBass = getAverage(freqs, 80, 320) 132 | const midrange = getAverage(freqs, 320, 1280) 133 | const lowTreble = getAverage(freqs, 4000, 4500) 134 | // const highTreble = getAverage(freqs, 20480, sampleRate) 135 | 136 | for (var i=0; i < freqLow.length; i++) { 137 | freqLow[i] = lowBass 138 | freqMid[i] = midrange 139 | freqHigh[i] = lowTreble 140 | } 141 | 142 | attribList.forEach(attrib => { 143 | attrib.needsUpdate = true 144 | }) 145 | } 146 | }) 147 | -------------------------------------------------------------------------------- /src/5.js: -------------------------------------------------------------------------------- 1 | const getContext = require('get-canvas-context') 2 | const triangle = require('a-big-triangle') 3 | const createTexture = require('gl-texture2d') 4 | const createShader = require('gl-shader') 5 | const loadImage = require('img') 6 | const createApp = require('canvas-loop') 7 | const mapLimit = require('map-limit') 8 | const mouse = require('touch-position')() 9 | const clamp = require('clamp') 10 | const words = require('superb').words.filter(x => x.length <= 5) 11 | 12 | const glslify = require('glslify') 13 | const frag = glslify('./shaders/5.frag') 14 | const vert = glslify('./shaders/5.vert') 15 | 16 | const size = 256 17 | const gl = getContext('webgl') 18 | const canvas = gl.canvas 19 | 20 | document.body.appendChild(canvas) 21 | 22 | canvas.addEventListener('touchstart', ev => ev.preventDefault()) 23 | 24 | const shader = createShader(gl, vert, frag) 25 | 26 | let textures 27 | const presets = [ 28 | ['assets/3.jpg', 'assets/glass.jpg'] 29 | ] 30 | 31 | let ptr = 0 32 | load(...presets[ptr++], start) 33 | const click = () => load(...presets[ptr++ % presets.length]) 34 | 35 | window.addEventListener('click', click) 36 | window.addEventListener('touchstart', click) 37 | 38 | function load (background, displacement, cb) { 39 | if (textures) { 40 | textures.forEach(t => t.dispose()) 41 | } 42 | mapLimit([ background, displacement ], 25, loadImage, (err, results) => { 43 | if (err) return alert(err.message) 44 | 45 | let backgroundImage = results[0] 46 | let displaceImage = results[1] 47 | 48 | if (ptr % 2 === 0) { 49 | const { width, height } = backgroundImage 50 | const ctx = getContext('2d', { width, height}) 51 | const fontSize = 150 52 | ctx.font = fontSize + 'px "Noto Sans", Helvetica, sans-serif' 53 | ctx.fillStyle = 'black' 54 | ctx.fillRect(0, 0, width, height) 55 | ctx.fillStyle = 'white' 56 | ctx.textBaseline = 'middle' 57 | ctx.textAlign = 'center' 58 | ctx.shadowColor = 'white' 59 | ctx.shadowBlur = 25 60 | ctx.fillText(words[ptr % words.length], width / 2, height / 2) 61 | displaceImage = ctx.canvas 62 | } 63 | 64 | textures = [ backgroundImage, displaceImage ].map(img => { 65 | const texture = createTexture(gl, img) 66 | texture.minFilter = gl.LINEAR 67 | texture.magFilter = gl.LINEAR 68 | texture.wrap = gl.REPEAT 69 | return texture 70 | }) 71 | 72 | if (typeof cb === 'function') cb() 73 | }) 74 | } 75 | 76 | 77 | 78 | function start () { 79 | createApp(canvas, { 80 | parent: () => [ size, size ], 81 | scale: 2 82 | }).on('tick', render).start() 83 | } 84 | 85 | function render () { 86 | gl.clearColor(0, 0, 0, 1) 87 | gl.clear(gl.COLOR_BUFFER_BIT) 88 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight) 89 | shader.bind() 90 | shader.uniforms.colorBuffer = 0 91 | shader.uniforms.displacement = 1 92 | 93 | const scale = 0.05 94 | shader.uniforms.offset = [ 95 | clamp(scale * ((mouse[0] / window.innerWidth) * 2.0 - 1.0), -0.25, 0.25), 96 | clamp(scale * ((mouse[1] / window.innerHeight) * 2.0 - 1.0), -0.25, 0.25) 97 | ] 98 | 99 | canvas.style.left = Math.round((window.innerWidth - size) / 2) + 'px' 100 | canvas.style.top = Math.round((window.innerHeight - size) / 2) + 'px' 101 | textures.forEach((x, i) => x.bind(i)) 102 | triangle(gl) 103 | } 104 | -------------------------------------------------------------------------------- /src/7.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | const random = require('random-float') 3 | const geoPieceRing = require('geo-piecering') 4 | const geoArc = require('geo-arc') 5 | const geoChevron = require('geo-chevron') 6 | const createComplex = require('three-simplicial-complex')(THREE) 7 | const createApp = require('./three-orbit-app') 8 | const { PI } = Math 9 | 10 | const app = createApp({ 11 | distance: 9, 12 | position: [-2.9, 3.0, 3.5], 13 | near: 0.01, 14 | distanceBounds: [ 0.25, 20 ], 15 | far: 100 16 | }) 17 | 18 | app.renderer.setClearColor(0x31a9ee, 1.0) 19 | 20 | const materials = [ 21 | new THREE.MeshBasicMaterial({ 22 | wireframe: true, 23 | transparent: true, 24 | opacity: 1, 25 | side: THREE.DoubleSide 26 | }), 27 | new THREE.MeshBasicMaterial({ 28 | transparent: true, 29 | opacity: 1, 30 | side: THREE.DoubleSide 31 | }) 32 | ] 33 | const meshes = [] 34 | 35 | function addCore () { 36 | addGeom(geoPieceRing({ 37 | y: random(-3.5, 3), 38 | height: random(0.01, 1.0), 39 | radius: random(0.1, 1.5), 40 | numPieces: Math.floor(random(5, 20)), 41 | quadsPerPiece: 1, 42 | pieceSize: (PI * 2) * 1 / random(20, 40) 43 | })) 44 | 45 | const radius = random(0, 2) 46 | addGeom(geoArc({ 47 | y: random(-3.5, 3), 48 | startRadian: random(-PI, PI), 49 | endRadian: random(-PI, PI), 50 | innerRadius: radius, 51 | outerRadius: radius + random(0.05, 0.15), 52 | numBands: 4, 53 | numSlices: 90, 54 | })) 55 | } 56 | 57 | function addArrows (offset) { 58 | return addGeom(geoChevron({ 59 | startRadian: 0, 60 | width: 1, 61 | depth: 1, 62 | thickness: 0.5 63 | }), { offset: offset, mirror: true, material: new THREE.MeshBasicMaterial({ 64 | color: 0xffffff, side: THREE.DoubleSide 65 | }) }) 66 | } 67 | 68 | const arrows = addArrows(3) 69 | arrows.rotationFactor = 0.1 70 | 71 | for (var i = 0; i < 32; i++) { 72 | addCore() 73 | } 74 | 75 | function addGeom (complex, opt) { 76 | opt = opt || {} 77 | const geom = createComplex(complex) 78 | 79 | let mat 80 | if (opt.material) { 81 | mat = opt.material 82 | } else { 83 | mat = materials[Math.floor(Math.random() * materials.length)].clone() 84 | mat.opacity = random(0.5, 1.0) 85 | } 86 | 87 | let mesh = new THREE.Mesh(geom, mat) 88 | mesh.position.fromArray(opt.position || [0, 0, 0]) 89 | 90 | if (opt.mirror) { 91 | const offset = opt.offset || 0 92 | const group = new THREE.Object3D() 93 | for (var i = 0; i < 4; i++) { 94 | const a = PI * 2 * (i / 4) 95 | const m2 = mesh.clone() 96 | m2.rotation.y = -a 97 | m2.position.x = Math.cos(a) * offset 98 | m2.position.z = Math.sin(a) * offset 99 | group.add(m2) 100 | } 101 | meshes.push(group) 102 | mesh = group 103 | } else { 104 | meshes.push(mesh) 105 | } 106 | mesh.rotationFactor = random(-1, 1) 107 | return mesh 108 | } 109 | 110 | meshes.forEach(m => app.scene.add(m)) 111 | 112 | app.on('tick', (dt) => { 113 | meshes.forEach((m) => { 114 | m.rotation.y += (dt / 1000) * m.rotationFactor 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /src/audio-util.js: -------------------------------------------------------------------------------- 1 | const clamp = require('clamp') 2 | 3 | export function index2freq (n, sampleRate, fftSize) { 4 | return n * sampleRate / fftSize 5 | } 6 | 7 | export function freq2index (freq, sampleRate, fftSize) { 8 | return clamp(Math.floor(freq / (sampleRate / fftSize)), 0, fftSize / 2) 9 | } 10 | 11 | export function getAnalyserAverages (analyser, minHz, maxHz) { 12 | const node = Array.isArray(analyser.analyser) ? analyser.analyser[0] : analyser.analyser 13 | const sampleRate = analyser.ctx.sampleRate 14 | const fftSize = node.fftSize 15 | const freqs = analyser.frequencies() 16 | return getAverageFrom(freqs, minHz, maxHz, sampleRate, fftSize) 17 | } 18 | 19 | export function frequencyAverages (sampleRate, fftSize) { 20 | return function getAverage (freqs, minHz, maxHz) { 21 | return getAverageFrom(freqs, minHz, maxHz, sampleRate, fftSize) 22 | } 23 | } 24 | 25 | 26 | function getAverageFrom (freqs, minHz, maxHz, sampleRate, fftSize) { 27 | let start = freq2index(minHz, sampleRate, fftSize) 28 | let end = freq2index(maxHz, sampleRate, fftSize) 29 | const count = end - start 30 | let sum = 0 31 | for (; start < end; start++) { 32 | sum += freqs[start] / 255 33 | } 34 | return count === 0 ? 0 : (sum / count) 35 | } -------------------------------------------------------------------------------- /src/desktop-only.js: -------------------------------------------------------------------------------- 1 | const fatal = require('./fatal-error')() 2 | 3 | module.exports = error 4 | function error (err) { 5 | if (err) console.error(err) 6 | return fatal(` 7 |
Only supported on Desktop Chrome & FireFox.
8 |
See my other #codevember demos at 9 | 10 | https://github.com/mattdesl/codevember.
11 | `) 12 | } -------------------------------------------------------------------------------- /src/fatal-error.js: -------------------------------------------------------------------------------- 1 | var insertCSS = require('insert-css') 2 | var assign = require('object-assign') 3 | var domify = require('domify') 4 | var once = require('once') 5 | var fonts = require('google-fonts') 6 | 7 | var css = ` 8 | #fatal-error { 9 | position: fixed; 10 | width: 100%; 11 | height: 100%; 12 | z-index: 100000; 13 | top: 0; 14 | left: 0; 15 | padding: 20px; 16 | box-sizing: border-box; 17 | font-size: 16px; 18 | margin: 0; 19 | color: #000; 20 | } 21 | .fatal-error-stack-line { 22 | padding-left: 20px; 23 | } 24 | .fatal-error-stack-line:first-child { 25 | padding-left: 0px; 26 | font-weight: bold; 27 | } 28 | a, a:visited, a:active { 29 | text-decoration: none; 30 | color: #48a0cd; 31 | } 32 | a:hover { 33 | text-decoration: underline; 34 | } 35 | ` 36 | 37 | module.exports = createErrorPage 38 | function createErrorPage (opt) { 39 | opt = opt || {} 40 | var dark = opt.dark 41 | var pre = opt.pre 42 | var googleFonts = opt.googleFonts !== false 43 | var stack = opt.stack 44 | 45 | var setupCSS = once(function setupCSS () { 46 | if (googleFonts) { 47 | fonts.add({ 'Open Sans': [300, 600] }) 48 | } 49 | insertCSS(css) 50 | }) 51 | 52 | function create () { 53 | setupCSS() 54 | 55 | var element = document.createElement('div') 56 | element.setAttribute('id', 'fatal-error') 57 | document.body.appendChild(element) 58 | 59 | assign(element.style, { 60 | font: pre 61 | ? '16px monospace' 62 | : '15px "Open Sans", Helvetica, sans-serif', 63 | background: dark ? '#313131' : '#fff', 64 | color: dark ? '#e9e9e9' : '#000', 65 | 'word-wrap': pre ? 'break-word' : undefined 66 | }) 67 | 68 | return element 69 | } 70 | 71 | function splitStack (lines) { 72 | return lines.map(function (str) { 73 | return '
' + 74 | str + '
' 75 | }).join('\n') 76 | } 77 | 78 | return function showError (err) { 79 | var msg = err 80 | if (err instanceof Error) { 81 | msg = stack ? err.stack : err.message 82 | } 83 | 84 | if (typeof msg === 'string') { 85 | msg = (msg || '').trim() 86 | if (stack) { 87 | var lines = msg.split('\n') 88 | if (lines.length > 0) msg = splitStack(lines) 89 | } 90 | } 91 | 92 | var element = document.querySelector('#fatal-error') 93 | if (!element) { 94 | element = create() 95 | } 96 | 97 | while (element.firstChild) { // clear children 98 | element.removeChild(element.firstChild) 99 | } 100 | 101 | if (Array.isArray(msg)) { 102 | msg.forEach(function (line) { 103 | element.appendChild(domify(line)) 104 | }) 105 | } else { 106 | element.appendChild(domify(msg)) 107 | } 108 | return element 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/geom-test.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | const loadJson = require('load-json-xhr') 3 | const createApp = require('./three-orbit-app') 4 | const fatal = require('./fatal-error')() 5 | 6 | const app = createApp({ 7 | canvas: document.querySelector('canvas') 8 | }) 9 | 10 | loadGeometry('assets/mesh/elk.json', (err, geom) => { 11 | if (err) return fatal(err) 12 | console.log('ready') 13 | }) 14 | 15 | function setup () { 16 | 17 | } 18 | 19 | function loadGeometry (path, cb) { 20 | const loader = new THREE.JSONLoader(); 21 | loadJson(path, (err, data) => { 22 | if (err) return cb(err) 23 | else cb(null, loader.parse(data)) 24 | }) 25 | } -------------------------------------------------------------------------------- /src/gl/DeviceOrientationControls.js: -------------------------------------------------------------------------------- 1 | module.exports = DeviceOrientationControls 2 | function DeviceOrientationControls ( object ) { 3 | 4 | var scope = this; 5 | 6 | this.object = object; 7 | this.object.rotation.reorder( "YXZ" ); 8 | 9 | this.enabled = true; 10 | 11 | this.deviceOrientation = {}; 12 | this.screenOrientation = 0; 13 | 14 | var onDeviceOrientationChangeEvent = function ( event ) { 15 | 16 | scope.deviceOrientation = event; 17 | 18 | }; 19 | 20 | var onScreenOrientationChangeEvent = function () { 21 | 22 | scope.screenOrientation = window.orientation || 0; 23 | 24 | }; 25 | 26 | // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y'' 27 | 28 | var setObjectQuaternion = function () { 29 | 30 | var zee = new THREE.Vector3( 0, 0, 1 ); 31 | 32 | var euler = new THREE.Euler(); 33 | 34 | var q0 = new THREE.Quaternion(); 35 | 36 | var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis 37 | 38 | return function ( quaternion, alpha, beta, gamma, orient ) { 39 | 40 | euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us 41 | 42 | quaternion.setFromEuler( euler ); // orient the device 43 | 44 | quaternion.multiply( q1 ); // camera looks out the back of the device, not the top 45 | 46 | quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation 47 | 48 | } 49 | 50 | }(); 51 | 52 | this.connect = function() { 53 | 54 | onScreenOrientationChangeEvent(); // run once on load 55 | 56 | window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); 57 | window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); 58 | 59 | scope.enabled = true; 60 | 61 | }; 62 | 63 | this.disconnect = function() { 64 | 65 | window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); 66 | window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); 67 | 68 | scope.enabled = false; 69 | 70 | }; 71 | 72 | this.update = function () { 73 | 74 | if ( scope.enabled === false ) return; 75 | 76 | var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) : 0; // Z 77 | var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad( scope.deviceOrientation.beta ) : 0; // X' 78 | var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0; // Y'' 79 | var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0; // O 80 | 81 | setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient ); 82 | 83 | }; 84 | 85 | this.dispose = function () { 86 | 87 | this.disconnect(); 88 | 89 | }; 90 | 91 | this.connect(); 92 | 93 | }; -------------------------------------------------------------------------------- /src/gl/TextGeometry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author zz85 / http://www.lab4games.net/zz85/blog 3 | * @author alteredq / http://alteredqualia.com/ 4 | * 5 | * For creating 3D text geometry in three.js 6 | * 7 | * Text = 3D Text 8 | * 9 | * parameters = { 10 | * size: , // size of the text 11 | * height: , // thickness to extrude text 12 | * curveSegments: , // number of points on the curves 13 | * 14 | * font: , // font name 15 | * weight: , // font weight (normal, bold) 16 | * style: , // font style (normal, italics) 17 | * 18 | * bevelEnabled: , // turn on bevel 19 | * bevelThickness: , // how deep into text bevel goes 20 | * bevelSize: , // how far from text outline is bevel 21 | * } 22 | * 23 | */ 24 | 25 | /* Usage Examples 26 | 27 | // TextGeometry wrapper 28 | 29 | var text3d = new TextGeometry( text, options ); 30 | 31 | // Complete manner 32 | 33 | var textShapes = THREE.FontUtils.generateShapes( text, options ); 34 | var text3d = new ExtrudeGeometry( textShapes, options ); 35 | 36 | */ 37 | 38 | THREE.TextGeometry = function ( text, parameters ) { 39 | 40 | parameters = parameters || {}; 41 | 42 | var textShapes = THREE.FontUtils.generateShapes( text, parameters ); 43 | 44 | // translate parameters to ExtrudeGeometry API 45 | 46 | parameters.amount = parameters.height !== undefined ? parameters.height : 50; 47 | 48 | // defaults 49 | 50 | if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; 51 | if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; 52 | if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; 53 | 54 | THREE.ExtrudeGeometry.call( this, textShapes, parameters ); 55 | 56 | this.type = 'TextGeometry'; 57 | 58 | }; 59 | 60 | THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); 61 | THREE.TextGeometry.prototype.constructor = THREE.TextGeometry; 62 | 63 | module.exports = THREE.TextGeometry; -------------------------------------------------------------------------------- /src/gl/ThreeLine25D.js: -------------------------------------------------------------------------------- 1 | var inherits = require('inherits') 2 | var getNormals = require('polyline-normals') 3 | var VERTS_PER_POINT = 3 4 | 5 | var tmp = [0, 0] 6 | 7 | module.exports = function(THREE) { 8 | 9 | function LineMesh(path, opt) { 10 | if (!(this instanceof LineMesh)) 11 | return new LineMesh(path, opt) 12 | THREE.BufferGeometry.call(this) 13 | 14 | if (Array.isArray(path)) { 15 | opt = opt||{} 16 | } else if (typeof path === 'object') { 17 | opt = path 18 | path = [] 19 | } 20 | 21 | opt = opt||{} 22 | 23 | this._positions = new THREE.BufferAttribute(null, 3) 24 | this._normals = new THREE.BufferAttribute(null, 2) 25 | this._miters = new THREE.BufferAttribute(null, 1) 26 | this._indices = new THREE.BufferAttribute(null, 1) 27 | 28 | if (opt.distances) 29 | this._distances = new THREE.BufferAttribute(null, 1) 30 | 31 | this.update(path, opt.closed) 32 | 33 | this.addAttribute('position', this._positions) 34 | this.addAttribute('lineNormal', this._normals) 35 | this.addAttribute('lineMiter', this._miters) 36 | this.addAttribute('index', this._indices) 37 | 38 | if (opt.distances) 39 | this.addAttribute('lineDistance', this._distances) 40 | } 41 | 42 | inherits(LineMesh, THREE.BufferGeometry) 43 | 44 | LineMesh.prototype.update = function(path, closed) { 45 | path = path||[] 46 | var normals = getNormals(path, closed) 47 | 48 | if (closed) { 49 | path = path.slice() 50 | path.push(path[0]) 51 | normals.push(normals[0]) 52 | } 53 | 54 | if (!this._positions.array || 55 | (path.length !== this._positions.array.length/3/VERTS_PER_POINT)) { 56 | var count = path.length * VERTS_PER_POINT 57 | this._positions.array = new Float32Array(count * 3) 58 | this._normals.array = new Float32Array(count * 2) 59 | this._miters.array = new Float32Array(count * 1) 60 | this._indices.array = new Uint16Array(Math.max(0, (path.length-1) * 6)) 61 | 62 | if (this._distances) 63 | this._distances.array = new Float32Array(count * 1) 64 | } 65 | var useDist = Boolean(this._distances) 66 | 67 | this._positions.needsUpdate = true 68 | this._miters.needsUpdate = true 69 | this._normals.needsUpdate = true 70 | this._indices.needsUpdate = true 71 | if (useDist) 72 | this._distances.needsUpdate = true 73 | 74 | var index = 0, 75 | c = 0, 76 | dIndex = 0, 77 | indexArray = this._indices.array 78 | 79 | path.forEach(function(point, pointIndex, self) { 80 | var i = index 81 | indexArray[c++] = i + 0 82 | indexArray[c++] = i + 1 83 | indexArray[c++] = i + 2 84 | indexArray[c++] = i + 2 85 | indexArray[c++] = i + 1 86 | indexArray[c++] = i + 3 87 | 88 | this._positions.setXYZ(index++, point[0], point[1], point[2]) 89 | this._positions.setXYZ(index++, point[0], point[1], point[2]) 90 | 91 | if (useDist) { 92 | var d = pointIndex/(self.length-1) 93 | this._distances.setX(dIndex++, d) 94 | this._distances.setX(dIndex++, d) 95 | } 96 | }, this) 97 | 98 | var nIndex = 0, 99 | mIndex = 0 100 | normals.forEach(function(n) { 101 | var norm = n[0] 102 | var miter = n[1] 103 | this._normals.setXY(nIndex++, norm[0], norm[1]) 104 | this._normals.setXY(nIndex++, norm[0], norm[1]) 105 | 106 | this._miters.setX(mIndex++, -miter) 107 | this._miters.setX(mIndex++, miter) 108 | }, this) 109 | } 110 | return LineMesh 111 | } -------------------------------------------------------------------------------- /src/gl/gl-line-3d.js: -------------------------------------------------------------------------------- 1 | const createBuffer = require('gl-buffer') 2 | const createVAO = require('gl-vao') 3 | 4 | const pack = require('array-pack-2d') 5 | const identity = require('gl-mat4/identity') 6 | const clamp = require('clamp') 7 | 8 | module.exports = function (gl, shader, path) { 9 | if (!shader) throw new TypeError('need to pass a shader') 10 | shader.bind() 11 | shader.attributes.position.location = 0 12 | shader.attributes.direction.location = 1 13 | shader.attributes.next.location = 2 14 | shader.attributes.previous.location = 3 15 | 16 | // each vertex has the following attribs: 17 | // vec3 position //current point on line 18 | // vec3 previous //previous point on line 19 | // vec3 next //next point on line 20 | // float direction //a sign, -1 or 1 21 | 22 | // we submit two vertices per point so that 23 | // we can expand them away from each other 24 | let indexBuffer = emptyBuffer(Uint16Array, gl.ELEMENT_ARRAY_BUFFER) 25 | let positionBuffer = emptyBuffer() 26 | let previousBuffer = emptyBuffer() 27 | let nextBuffer = emptyBuffer() 28 | let directionBuffer = emptyBuffer() 29 | let count = 0 30 | let vao = createVAO(gl) 31 | 32 | // default uniforms 33 | let model = identity([]) 34 | let projection = identity([]) 35 | let view = identity([]) 36 | let thickness = 1 37 | let aspect = 1 38 | let miter = 0 39 | let color = [1, 1, 1] 40 | 41 | if (path) update(path) 42 | 43 | return { 44 | update, 45 | model, 46 | view, 47 | projection, 48 | thickness, 49 | color, 50 | miter, 51 | aspect, 52 | 53 | draw () { 54 | shader.bind() 55 | shader.uniforms.model = this.model 56 | shader.uniforms.view = this.view 57 | shader.uniforms.projection = this.projection 58 | shader.uniforms.color = this.color 59 | shader.uniforms.thickness = this.thickness 60 | shader.uniforms.aspect = this.aspect 61 | shader.uniforms.miter = this.miter 62 | 63 | vao.bind() 64 | vao.draw(gl.TRIANGLES, count) 65 | vao.unbind() 66 | } 67 | } 68 | 69 | // in real-world you wouldn't want to create so 70 | // many typed arrays per frame 71 | function update (path) { 72 | // ensure 3 component vectors 73 | if (path.length > 0 && path[0].length !== 3) { 74 | path = path.map(point => { 75 | let [x, y, z] = point 76 | return [x || 0, y || 0, z || 0] 77 | }) 78 | } 79 | 80 | count = (path.length - 1) * 6 81 | 82 | // each pair has a mirrored direction 83 | let direction = duplicate(path.map(x => 1), true) 84 | // now get the positional data for each vertex 85 | let positions = duplicate(path) 86 | let previous = duplicate(path.map(relative(-1))) 87 | let next = duplicate(path.map(relative(+1))) 88 | let indexUint16 = createIndices(path.length) 89 | 90 | // now update the buffers with float/short data 91 | positionBuffer.update(pack(positions)) 92 | previousBuffer.update(pack(previous)) 93 | nextBuffer.update(pack(next)) 94 | directionBuffer.update(pack(direction)) 95 | indexBuffer.update(indexUint16) 96 | 97 | vao.update([ 98 | { buffer: positionBuffer, size: 3 }, 99 | { buffer: directionBuffer, size: 1 }, 100 | { buffer: nextBuffer, size: 3 }, 101 | { buffer: previousBuffer, size: 3 } 102 | ], indexBuffer) 103 | } 104 | 105 | function emptyBuffer (ArrayType, type) { 106 | ArrayType = ArrayType || Float32Array 107 | return createBuffer(gl, new ArrayType(), type || gl.ARRAY_BUFFER, gl.STATIC_DRAW) 108 | } 109 | } 110 | 111 | function relative (offset) { 112 | return (point, index, list) => { 113 | index = clamp(index + offset, 0, list.length - 1) 114 | return list[index] 115 | } 116 | } 117 | 118 | function duplicate (nestedArray, mirror) { 119 | var out = [] 120 | nestedArray.forEach(x => { 121 | let x1 = mirror ? -x : x 122 | out.push(x1, x) 123 | }) 124 | return out 125 | } 126 | 127 | // counter-clockwise indices but prepared for duplicate vertices 128 | function createIndices (length) { 129 | let indices = new Uint16Array(length * 6) 130 | let c = 0 131 | let index = 0 132 | for (let j = 0; j < length; j++) { 133 | let i = index 134 | indices[c++] = i + 0 135 | indices[c++] = i + 1 136 | indices[c++] = i + 2 137 | indices[c++] = i + 2 138 | indices[c++] = i + 1 139 | indices[c++] = i + 3 140 | index += 2 141 | } 142 | return indices 143 | } 144 | -------------------------------------------------------------------------------- /src/gl/motion.js: -------------------------------------------------------------------------------- 1 | var vec = require('gl-vec2') 2 | var number = require('as-number') 3 | 4 | var World = require('verlet-system') 5 | var Point = require('verlet-point') 6 | var newArray = require('new-array') 7 | var randf = require('random-float') 8 | 9 | var Simplex = require('simplex-sampler') 10 | 11 | var tmp = [0,0] 12 | 13 | function Motion(opt) { 14 | opt = opt||{} 15 | this.world = World() 16 | 17 | this._noise = new Simplex(256) 18 | this._smooth = true 19 | this._seamless = true 20 | this._noise.generate() 21 | 22 | this.speed = 1 23 | 24 | var count = number(opt.count, 50) 25 | this.points = newArray(count).map(function() { 26 | var p = Point({ position: [Math.random(), Math.random()] }) 27 | p.samplePosition = [Math.random(), Math.random()] 28 | p.rotation = 0 29 | p.time = Math.random() 30 | p.noise = 0 31 | p.duration = Math.random()*2 32 | p.speed = Math.random() 33 | p.white = 0.0 34 | p.size = 1 35 | p.color = [0,0,0] 36 | p.fboColor = [0,0,0] 37 | 38 | var f = 0.001 39 | p.addForce([ f*randf(-1,1), f*randf(-1,1) ]) 40 | return p 41 | }) 42 | } 43 | 44 | Motion.prototype.update = function(dt) { 45 | this.world.integrate(this.points, dt) 46 | 47 | this.points.forEach(function(p) { 48 | var noise = this._noise 49 | 50 | var px = p.position[0], 51 | py = p.position[1], 52 | noiseSize = noise.size; 53 | 54 | var n = noise.sample(px*noiseSize, py*noiseSize); 55 | var angle = n * Math.PI * 2 + p.rotation 56 | vec.set(tmp, Math.cos(angle), Math.sin(angle)) 57 | vec.normalize(tmp, tmp) 58 | vec.scale(tmp, tmp, 0.0002 * p.speed) 59 | p.addForce(tmp) 60 | p.noise = n 61 | 62 | p.rotation += 0.01 63 | p.time += dt 64 | 65 | var resetBounds = px < 0 || py < 0 || px > 1 || py > 1 66 | if (p.time > p.duration 67 | || resetBounds) { 68 | p.time = 0 69 | p.speed = Math.random()*this.speed 70 | p.duration = Math.random()*2 71 | // p.rotation = Math.random() 72 | 73 | vec.set(tmp, Math.random(), Math.random()) 74 | p.place(tmp) 75 | } 76 | }, this) 77 | } 78 | 79 | module.exports = function(opt) { 80 | return new Motion(opt) 81 | } -------------------------------------------------------------------------------- /src/gl/texel-coord-solid-angle.js: -------------------------------------------------------------------------------- 1 | module.exports = texelCoordSolidAngle 2 | 3 | // https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/ 4 | function areaElement (x, y) { 5 | return Math.atan2(x * y, Math.sqrt(x * x + y * y + 1)) 6 | } 7 | 8 | function texelCoordSolidAngle (u, v, size) { 9 | // scale up to [-1, 1] range (inclusive), offset by 0.5 to point to texel center. 10 | var _u = (2.0 * (u + 0.5) / size) - 1.0 11 | var _v = (2.0 * (v + 0.5) / size) - 1.0 12 | 13 | // fixSeams 14 | _u *= 1.0 - 1.0 / size 15 | _v *= 1.0 - 1.0 / size 16 | 17 | var invResolution = 1.0 / size 18 | 19 | // U and V are the -1..1 texture coordinate on the current face. 20 | // Get projected area for this texel 21 | var x0 = _u - invResolution 22 | var y0 = _v - invResolution 23 | var x1 = _u + invResolution 24 | var y1 = _v + invResolution 25 | var solidAngle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1) 26 | 27 | // fixSeams cut 28 | if ((u === 0 && v === 0) || (u === size - 1 && v === 0) || (u === 0 && v === size - 1) || (u === size - 1 && v === size - 1)) { 29 | solidAngle /= 3 30 | } else if (u === 0 || v === 0 || u === size - 1 || v === size - 1) { 31 | solidAngle *= 0.5 32 | } 33 | 34 | return solidAngle 35 | } 36 | -------------------------------------------------------------------------------- /src/gl/three-face-centroid.js: -------------------------------------------------------------------------------- 1 | module.exports = faceCentroid 2 | function faceCentroid (vertices, face, out) { 3 | if (!out) out = new THREE.Vector3() 4 | const v1 = vertices[face.a] 5 | const v2 = vertices[face.b] 6 | const v3 = vertices[face.c] 7 | var x = (v1.x + v2.x + v3.x) / 3 8 | var y = (v1.y + v2.y + v3.y) / 3 9 | var z = (v1.z + v2.z + v3.z) / 3 10 | out.set(x, y, z) 11 | return out 12 | } 13 | -------------------------------------------------------------------------------- /src/gl/three-hdr-texture.js: -------------------------------------------------------------------------------- 1 | const xhr = require('xhr') 2 | const HDR = require('hdr').loader 3 | const xyz2rgb = require('./xyz-pixels-to-rgb') 4 | const toBuffer = require('arraybuffer-to-buffer') 5 | const xyz2rgbm = require('./xyz-pixels-to-rgbm') 6 | const noop = function () {} 7 | 8 | module.exports = threeHdrTexture 9 | function threeHdrTexture (uri, cb) { 10 | cb = cb || noop 11 | const texture = new THREE.DataTexture(1, 1) 12 | texture.format = THREE.RGBAFormat 13 | texture.type = THREE.UnsignedByteType 14 | xhr({ uri: uri, responseType: 'arraybuffer' }, function (err, resp, data) { 15 | if (!/^2/.test(resp.statusCode)) err = new Error('status code ' + resp.statusCode) 16 | if (err) return cb(err) 17 | 18 | var hdr = new HDR() 19 | hdr.once('load', function () { 20 | var rgbm = xyz2rgbm(this.data) 21 | texture.image = { 22 | data: rgbm, 23 | width: this.width, 24 | height: this.height 25 | } 26 | texture.needsUpdate = true 27 | cb(null, texture) 28 | cb = noop 29 | }) 30 | hdr.once('error', function () { 31 | cb(new Error('could not decode .hdr format')) 32 | cb = noop 33 | }) 34 | 35 | var buf = toBuffer(data) 36 | hdr.write(buf) 37 | hdr.end() 38 | }) 39 | return texture 40 | } 41 | -------------------------------------------------------------------------------- /src/gl/three-skybox.js: -------------------------------------------------------------------------------- 1 | const noop = function () {} 2 | 3 | module.exports = getSkybox 4 | function getSkybox (urls, cb = noop) { 5 | var cubeTex = THREE.ImageUtils.loadTextureCube(urls, undefined, 6 | (tex) => cb(null, tex), 7 | () => cb(new Error('error loading cube texture'))) 8 | 9 | var material = new THREE.ShaderMaterial({ 10 | uniforms: { 11 | 'tCube': { type: 't', value: cubeTex }, 12 | 'tFlip': { type: 'f', value: -1 } 13 | }, 14 | vertexShader: [ 15 | 'varying vec3 vWorldPosition;', 16 | THREE.ShaderChunk[ 'logdepthbuf_pars_vertex' ], 17 | 'void main() {', 18 | ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );', 19 | ' vWorldPosition = worldPosition.xyz;', 20 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 21 | THREE.ShaderChunk[ 'logdepthbuf_vertex' ], 22 | '}' 23 | ].join('\n'), 24 | fragmentShader: [ 25 | 'uniform samplerCube tCube;', 26 | 'uniform float tFlip;', 27 | 'varying vec3 vWorldPosition;', 28 | THREE.ShaderChunk[ 'logdepthbuf_pars_fragment' ], 29 | 'void main() {', 30 | ' gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );', 31 | THREE.ShaderChunk[ 'logdepthbuf_fragment' ], 32 | '}' 33 | ].join('\n'), 34 | fog: false, 35 | side: THREE.BackSide, 36 | depthWrite: false 37 | }) 38 | 39 | var geometry = new THREE.BoxGeometry(5000, 5000, 5000) 40 | return new THREE.Mesh(geometry, material) 41 | } 42 | -------------------------------------------------------------------------------- /src/gl/three-streetview-app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/codevember/b6ab14ec3cb827b9a95bd44107a09d4169d3ddd5/src/gl/three-streetview-app.js -------------------------------------------------------------------------------- /src/gl/three-vignette-background.js: -------------------------------------------------------------------------------- 1 | const glslify = require('glslify') 2 | 3 | const vert = glslify(` 4 | attribute vec3 position; 5 | uniform mat4 modelViewMatrix; 6 | uniform mat4 projectionMatrix; 7 | varying vec2 vUv; 8 | 9 | void main() { 10 | gl_Position = vec4(position, 1.0); 11 | vUv = vec2(position.x, position.y) * 0.5 + 0.5; 12 | } 13 | `, { inline: true }) 14 | const frag = glslify(` 15 | precision mediump float; 16 | 17 | #pragma glslify: grain = require('glsl-film-grain') 18 | #pragma glslify: blend = require('glsl-blend-soft-light') 19 | 20 | uniform vec3 color1; 21 | uniform vec3 color2; 22 | uniform float aspect; 23 | uniform vec2 scale; 24 | uniform float noiseAlpha; 25 | uniform bool aspectCorrection; 26 | uniform float grainScale; 27 | uniform float grainTime; 28 | uniform vec2 smooth; 29 | 30 | varying vec2 vUv; 31 | 32 | void main() { 33 | float gSize = 1.0 / grainScale; 34 | float g = grain(vUv, vec2(gSize * aspect, gSize), grainTime); 35 | 36 | vec2 q = vec2(vUv - 0.5); 37 | if (aspectCorrection) { 38 | q.x *= aspect; 39 | } 40 | q /= scale; 41 | float dst = length(q); 42 | dst = smoothstep(smooth.x, smooth.y, dst); 43 | 44 | vec3 color = mix(color1, color2, dst); 45 | vec3 noiseColor = blend(color, vec3(g)); 46 | 47 | gl_FragColor.rgb = mix(color, noiseColor, noiseAlpha); 48 | gl_FragColor.a = 1.0; 49 | } 50 | `, { inline: true }) 51 | 52 | module.exports = createBackground 53 | function createBackground (opt) { 54 | opt = opt || {} 55 | var geometry = new THREE.PlaneGeometry(2, 2, 1) 56 | var material = new THREE.RawShaderMaterial({ 57 | vertexShader: vert, 58 | fragmentShader: frag, 59 | side: THREE.DoubleSide, 60 | uniforms: { 61 | aspectCorrection: { type: 'i', value: false }, 62 | aspect: { type: 'f', value: 1 }, 63 | grainScale: { type: 'f', value: 0.005 }, 64 | grainTime: { type: 'f', value: 0 }, 65 | noiseAlpha: { type: 'f', value: 0.25 }, 66 | scale: { type: 'v2', value: new THREE.Vector2(1.5, 1.5) }, 67 | smooth: { type: 'v2', value: new THREE.Vector2(0.0, 1.0) }, 68 | color1: { type: 'c', value: new THREE.Color('#fff') }, 69 | color2: { type: 'c', value: new THREE.Color('#283844') } 70 | }, 71 | depthTest: false 72 | }) 73 | var mesh = new THREE.Mesh(geometry, material) 74 | mesh.style = style 75 | if (opt) mesh.style(opt) 76 | return mesh 77 | 78 | function style (opt) { 79 | opt = opt || {} 80 | if (Array.isArray(opt.colors)) { 81 | const colors = opt.colors.map(function (c) { 82 | if (typeof c === 'string' || typeof c === 'number') { 83 | return new THREE.Color(c) 84 | } 85 | return c 86 | }) 87 | material.uniforms.color1.value.copy(colors[0]) 88 | material.uniforms.color2.value.copy(colors[1]) 89 | } 90 | if (typeof opt.aspect === 'number') { 91 | material.uniforms.aspect.value = opt.aspect 92 | } 93 | if (typeof opt.grainScale === 'number') { 94 | material.uniforms.grainScale.value = opt.grainScale 95 | } 96 | if (typeof opt.grainTime === 'number') { 97 | material.uniforms.grainTime.value = opt.grainTime 98 | } 99 | if (opt.smooth) { 100 | const smooth = fromArray(opt.smooth, THREE.Vector2) 101 | material.uniforms.smooth.value.copy(smooth) 102 | } 103 | if (typeof opt.scale !== 'undefined') { 104 | let scale = opt.scale 105 | if (typeof scale === 'number') { 106 | scale = [ scale, scale ] 107 | } 108 | scale = fromArray(scale, THREE.Vector2) 109 | material.uniforms.scale.value.copy(scale) 110 | } 111 | if (typeof opt.aspectCorrection !== 'undefined') { 112 | material.uniforms.aspectCorrection.value = Boolean(opt.aspectCorrection) 113 | } 114 | } 115 | 116 | function fromArray (array, VectorType) { 117 | if (Array.isArray(array)) { 118 | return new VectorType().fromArray(array) 119 | } 120 | return array 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/gl/unindex-geometry.js: -------------------------------------------------------------------------------- 1 | // via @samsy 2 | module.exports = unindexGeometry 3 | function unindexGeometry (geometry) { 4 | let vertices = geometry.vertices 5 | let faces = geometry.faces 6 | let vertexUv = geometry.faceVertexUvs[0] 7 | let temp = new THREE.Geometry() 8 | 9 | var c = 0 10 | for (var i = 0; i < faces.length; i++) { 11 | temp.vertices.push(vertices[faces[i].a].clone()) 12 | temp.vertices.push(vertices[faces[i].b].clone()) 13 | temp.vertices.push(vertices[faces[i].c].clone()) 14 | temp.faceVertexUvs[0][i] = vertexUv[i] 15 | 16 | let face = new THREE.Face3(c++, c++, c++) 17 | if (faces[i].vertexNormals[0]) { 18 | face.vertexNormals[0] = faces[i].vertexNormals[0].clone() 19 | face.vertexNormals[1] = faces[i].vertexNormals[1].clone() 20 | face.vertexNormals[2] = faces[i].vertexNormals[2].clone() 21 | } 22 | face.normal = faces[i].normal.clone() 23 | temp.faces[i] = face 24 | } 25 | 26 | return temp 27 | } 28 | -------------------------------------------------------------------------------- /src/gl/xyz-pixels-to-rgb.js: -------------------------------------------------------------------------------- 1 | var xyz = require('xyz-utils') 2 | var tmp = [0, 0, 0] 3 | var tmp2 = [0, 0, 0] 4 | module.exports = xyzToRGB 5 | 6 | function xyzToRGB (xyzArray) { 7 | var ret = new Float32Array(xyzArray.length) 8 | for (var i = 0; i < xyzArray.length; i += 3) { 9 | tmp[0] = xyzArray[i] 10 | tmp[1] = xyzArray[i + 1] 11 | tmp[2] = xyzArray[i + 2] 12 | xyz.toRGB(tmp, tmp2) 13 | ret[i] = tmp2[0] 14 | ret[i + 1] = tmp2[1] 15 | ret[i + 2] = tmp2[2] 16 | } 17 | return ret 18 | } 19 | -------------------------------------------------------------------------------- /src/gl/xyz-pixels-to-rgbm.js: -------------------------------------------------------------------------------- 1 | var xyz = require('xyz-utils') 2 | var tmp = [0, 0, 0] 3 | var tmp1 = [0, 0, 0] 4 | var tmpRGBM = [0, 0, 0, 0] 5 | const clamp = require('clamp') 6 | 7 | module.exports = xyzToRGBM 8 | function xyzToRGBM (xyzArray) { 9 | var pixelCount = xyzArray.length / 3 10 | var ret = new Uint8Array(pixelCount * 4) 11 | for (var i = 0; i < pixelCount; i++) { 12 | tmp[0] = xyzArray[i * 3] 13 | tmp[1] = xyzArray[i * 3 + 1] 14 | tmp[2] = xyzArray[i * 3 + 2] 15 | xyz.toRGB(tmp, tmp1) // xyz to rgb 16 | encode(tmp1, tmpRGBM) // rgb to rgbm 17 | ret[i * 4] = tmpRGBM[0] 18 | ret[i * 4 + 1] = tmpRGBM[1] 19 | ret[i * 4 + 2] = tmpRGBM[2] 20 | ret[i * 4 + 3] = tmpRGBM[3] 21 | } 22 | return ret 23 | } 24 | 25 | function encode (color, out) { 26 | if (!out) out = [ 0, 0, 0, 0 ] 27 | var minB = 0.000001 28 | var coeff = 1 / 6 29 | var r = color[0] * coeff 30 | var g = color[1] * coeff 31 | var b = color[2] * coeff 32 | var a = clamp(Math.max(Math.max(r, g), Math.max(b, minB)), 0.0, 1.0) 33 | a = Math.ceil(a * 255.0) / 255.0 34 | out[0] = clamp(Math.ceil(r / a * 255), 0, 255) 35 | out[1] = clamp(Math.ceil(g / a * 255), 0, 255) 36 | out[2] = clamp(Math.ceil(b / a * 255), 0, 255) 37 | out[3] = clamp(Math.ceil(a * 255), 0, 255) 38 | return out 39 | } 40 | -------------------------------------------------------------------------------- /src/grid/calendar-grid.js: -------------------------------------------------------------------------------- 1 | const css = require('dom-css') 2 | const clamp = require('clamp') 3 | 4 | module.exports = function gridItems (grid, cells, opt = {}) { 5 | const aspect = typeof opt.aspect === 'number' ? opt.aspect : 1 6 | const padding = opt.padding || 0 7 | const margin = opt.margin || 0 8 | const maxSize = opt.maxSize || [ Infinity, Infinity ] 9 | const minSize = opt.minSize || [ -Infinity, -Infinity ] 10 | const maxCellSize = opt.maxCellSize || [ Infinity, Infinity ] 11 | const minCellSize = opt.minCellSize || [ -Infinity, -Infinity ] 12 | const style = opt.style || (() => {}) 13 | const solve = opt.solve || (() => { 14 | return [ 4, 7 ] 15 | }) 16 | 17 | const api = { 18 | padding, margin, resize 19 | } 20 | 21 | process.nextTick(resize) 22 | window.addEventListener('resize', resize) 23 | 24 | return api 25 | 26 | function resize () { 27 | const [ cols, rows ] = solve() 28 | resizeTo(cols, rows) 29 | } 30 | 31 | function resizeTo (cols, rows) { 32 | const padding = api.padding 33 | const margin = api.margin 34 | console.log(margin, padding) 35 | const padx = padding * (cols + 1) 36 | const pady = padding * (rows + 1) 37 | 38 | // parent bounds 39 | const width = clamp(window.innerWidth - margin * 2 - padx, minSize[0], maxSize[0]) 40 | // const height = clamp(window.innerHeight - margin * 2 - pady, minSize[1], maxSize[1]) 41 | 42 | const cellWidth = clamp(width / cols, minCellSize[0], maxCellSize[0]) 43 | const cellHeight = clamp(cellWidth / aspect, minCellSize[1], maxCellSize[1]) 44 | 45 | const gridWidth = cellWidth * cols + padx 46 | const gridHeight = cellHeight * rows + pady 47 | css(grid, { 48 | width: gridWidth, 49 | height: gridHeight 50 | }) 51 | 52 | cells.forEach((cell, i) => { 53 | var ix = Math.floor(i % cols) 54 | var iy = Math.floor(i / cols) 55 | const x = padding + ix * (padding + cellWidth) 56 | const y = padding + iy * (padding + cellHeight) 57 | css(cell, { 58 | left: x, 59 | top: y, 60 | width: cellWidth, 61 | height: cellHeight 62 | }) 63 | style(cell, cellWidth, cellHeight) 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/grid/content.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { url: 'http://mattdesl.github.io/codevember/1.html', 3 | name: 'Feather', description: 'Canvas 2D particle physics', mobile: true }, 4 | { url: 'http://mattdesl.github.io/codevember/2.html', name: 'Pollock', description: 'Ink splatter effect', mobile: true }, 5 | { url: 'http://mattdesl.github.io/codevember/3.html', name: 'Wave', description: '3D audio visualizer', audio: true }, 6 | { url: 'http://glslb.in/s/dadc0eb1', name: 'GLSL Sketch' }, 7 | { url: 'http://mattdesl.github.io/codevember/5.html', name: 'Displace', description: 'Displacement shader', mobile: true }, 8 | { url: 'http://mattdesl.github.io/codevember/6.html', name: 'Binaural', description: 'Binaural and reverb audio effects', audio: true, mobile: true }, 9 | { url: 'http://mattdesl.github.io/codevember/7.html', name: 'Reactor', description: 'Procedural 3D scene', mobile: true }, 10 | { url: 'http://mattdesl.github.io/codevember/8.html', name: 'Beatgrid', description: 'Audio beat detection', audio: true }, 11 | { url: 'http://mattdesl.github.io/codevember/9.html', name: 'Junk Pile', description: 'Generative canvas 2D rendering', mobile: true }, 12 | { url: 'http://glslb.in/s/40bb7029', name: 'GLSL Sketch' }, 13 | { url: 'http://mattdesl.github.io/codevember/11.html', name: 'Poppies', description: 'Abstract poppy field', mobile: true }, 14 | { url: 'http://glslb.in/s/9ad772d2', name: 'GLSL Sketch' }, 15 | { url: 'http://mattdesl.github.io/codevember/13.html', name: 'Party', description: 'Audio visualizer', audio: true }, 16 | { url: 'http://mattdesl.github.io/codevember/14.html', name: 'Laser Show', description: '3D laser shaders', mobile: true }, 17 | { url: 'http://mattdesl.github.io/codevember/15.html', name: 'Snowden', description: 'Quantized mesh with line rendering' }, 18 | { url: 'http://mattdesl.github.io/codevember/16.html', name: 'Radio', description: 'VU meter reacting to a podcast', audio: true }, 19 | { url: 'http://mattdesl.github.io/codevember/17.html', name: 'Mr cthulhu', description: 'stringy sphere', mobile: true }, 20 | { url: 'http://codepen.io/mattdesl/full/avMYMd/', name: 'Geometric', description: '2D geometric designs', mobile: true }, 21 | { url: 'http://mattdesl.github.io/codevember/19.html', name: 'AST', description: 'a self-visualizing program' }, 22 | { url: 'http://mattdesl.github.io/codevember/20.html', name: 'Polygraph', description: 'lines on paper', mobile: true }, 23 | { url: 'http://mattdesl.github.io/codevember/21.html', name: 'Silk', description: '3D audio visualizer', audio: true }, 24 | { url: 'http://mattdesl.github.io/codevember/22.html', name: 'Punched', description: 'Encoding text into stylized punched card graphics', mobile: true }, 25 | { url: 'http://mattdesl.github.io/codevember/23.html', name: 'Marvel', description: 'procedural SDF text effects' }, 26 | { url: 'http://jam3.github.io/web-audio-player/', name: 'web-audio-player', description: 'An open-source WebAudio library', mobile: true, audio: true }, 27 | { url: 'http://glslb.in/s/fe9a73e2', name: 'GLSL Sketch' }, 28 | { url: 'http://mattdesl.github.io/codevember/26.html', name: 'Streets', description: 'StreetView with 3D text', mobile: true }, 29 | { url: 'http://mattdesl.github.io/codevember/27.html', name: 'Orb', description: 'screen-space projected lines', mobile: true }, 30 | { url: 'http://mattdesl.github.io/codevember/28.html', name: 'Physical', description: 'StreetView as PBR material', mobile: true }, 31 | { url: 'http://mattdesl.github.io/codevember/29.html', name: 'Disco', description: 'Encoding HDR files as RGBM', mobile: true }, 32 | { url: 'http://mattdesl.github.io/codevember/30.html', name: 'Space', description: '3D audio visualizer', audio: true, mobile: true } 33 | ] 34 | -------------------------------------------------------------------------------- /src/grid/grid-item.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | {{#if mobile}} 5 | 6 | {{/if}} 7 | {{#if audio}} 8 | 9 | {{/if}} 10 |
11 |
{{day}}
12 |
{{description}}
13 |
{{name}}
14 |
-------------------------------------------------------------------------------- /src/grid/image-urls.js: -------------------------------------------------------------------------------- 1 | var sortByName = require('./sort-files') 2 | const fs = require('fs') 3 | let files = fs.readdirSync(__dirname + '/../../assets/thumbs') 4 | 5 | files = files.filter(f => /\.(jpe?g|png)$/i.test(f)) 6 | files = files.map(f => { 7 | return `assets/thumbs/${f}` 8 | }) 9 | files.sort(sortByName) 10 | 11 | module.exports = files 12 | -------------------------------------------------------------------------------- /src/grid/index.js: -------------------------------------------------------------------------------- 1 | const gridItem = require('./grid-item.hbs') 2 | const domify = require('domify') 3 | const content = require('./content') 4 | const calendar = require('./calendar-grid') 5 | const images = require('./image-urls') 6 | const palettes = require('../../assets/thumb-palettes.json') 7 | const css = require('dom-css') 8 | const colorStyle = require('color-style') 9 | const loadImage = require('img') 10 | const classes = require('dom-classes') 11 | const isMobile = require('../is-mobile')() 12 | 13 | const container = document.querySelector('#grid') 14 | 15 | if (!isMobile) { // cheap mobile test 16 | classes.add(document.body, 'no-touch') 17 | } 18 | 19 | const imageLoadDelay = 50 20 | const items = content.map((data, i) => { 21 | data.day = i + 1 22 | const element = domify(gridItem(data)) 23 | const [ r, g, b ] = palettes[i][0] 24 | const color = colorStyle(r, g, b) 25 | css(element, { backgroundColor: color }) 26 | imageLoader(element, i) 27 | return element 28 | }) 29 | 30 | items.forEach(item => container.appendChild(item)) 31 | 32 | const grid = calendar(container, items, { 33 | aspect: 1280 / 840, 34 | maxSize: [ 1200, 400 ], 35 | maxCellSize: [ Infinity, Infinity ], 36 | padding: 5, 37 | solve: () => { 38 | let count = 30 39 | let cols = 4 40 | if (window.innerWidth < 480) { 41 | cols = 1 42 | grid.margin = 10 43 | } else if (window.innerWidth < 720) { 44 | cols = 2 45 | grid.margin = 20 46 | } else { 47 | grid.margin = 40 48 | } 49 | const rows = Math.ceil(count / cols) 50 | return [ cols, rows ] 51 | }, 52 | style: (cell, width, height) => { 53 | css(cell.querySelector('.title'), { 54 | lineHeight: height + 'px' 55 | }) 56 | css(cell.querySelector('.description'), { 57 | lineHeight: height + 'px' 58 | }) 59 | } 60 | }) 61 | 62 | grid.resize() 63 | 64 | function imageLoader (element, index) { 65 | const imgEl = element.querySelector('.image') 66 | classes.add(imgEl, 'image-loading') 67 | const src = images[index] 68 | setTimeout(() => { 69 | loadImage(src, (err, img) => { 70 | if (err) throw err 71 | classes.remove(imgEl, 'image-loading') 72 | classes.add(imgEl, 'image-loaded') 73 | css(imgEl, { 74 | backgroundImage: `url("${src}")`, 75 | backgroundSize: 'cover', 76 | backgroundRepeat: 'none', 77 | backgroundPosition: 'center center' 78 | }) 79 | }) 80 | }, imageLoadDelay) 81 | } 82 | -------------------------------------------------------------------------------- /src/grid/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fff; 3 | font-family: 'Noto Sans', Helvetica, sans-serif; 4 | font-weight: 400; 5 | color: #000; 6 | margin: 0; 7 | margin-top: 20px; 8 | padding: 0; 9 | text-align: center; 10 | } 11 | 12 | #grid { 13 | color: #fff; 14 | text-align: left; 15 | display: inline-block; 16 | position: relative; 17 | margin: 0; 18 | margin-bottom: 40px; 19 | margin-top: 20px; 20 | padding: 0; 21 | } 22 | 23 | .no-touch .grid-item:hover > .image:after, 24 | .no-touch .grid-item:hover, 25 | .no-touch .grid-item:hover > .icons, 26 | .grid-item > .image:after, 27 | .transition-all { 28 | -webkit-transition: all 0.1s ease-out; 29 | -moz-transition: all 0.1s ease-out; 30 | -ms-transition: all 0.1s ease-out; 31 | -o-transition: all 0.1s ease-out; 32 | transition: all 0.1s ease-out; 33 | } 34 | 35 | .image-loading { 36 | opacity: 0.0; 37 | } 38 | .image-loaded { 39 | opacity: 1.0; 40 | } 41 | 42 | .grid-item { 43 | text-decoration: none; 44 | color: #fff; 45 | float: left; 46 | display: inline; 47 | position: absolute; 48 | background: #CCCCCC; 49 | margin: 0px; 50 | cursor: pointer; 51 | } 52 | 53 | .grid-item > .image:after { 54 | display: block; 55 | width: 100%; 56 | height: 100%; 57 | position: absolute; 58 | top: 0; 59 | left: 0; 60 | content: ' '; 61 | background: rgba(0, 0, 0, 0.65); 62 | } 63 | 64 | .no-touch .grid-item:hover { 65 | color: rgba(255, 255, 255, 0.0); 66 | } 67 | .no-touch .grid-item:hover > .icons { 68 | opacity: 0.0; 69 | } 70 | .no-touch .grid-item:hover > .image:after { 71 | background: rgba(0, 0, 0, 0.0); 72 | } 73 | 74 | .grid-item > .image { 75 | position: absolute; 76 | top: 0; 77 | left: 0; 78 | width: 100%; 79 | height: 100%; 80 | margin: 0; 81 | padding: 0; 82 | } 83 | 84 | .grid-item > .day { 85 | font-size: 8px; 86 | position: absolute; 87 | opacity: 0.5; 88 | top: 1vmax; 89 | left: 1vmax; 90 | } 91 | 92 | .grid-item > .title { 93 | position: relative; 94 | font-weight: 700; 95 | font-size: 18px; 96 | text-align: center; 97 | text-transform: lowercase; 98 | } 99 | 100 | .grid-item > .description { 101 | position: absolute; 102 | opacity: 0.5; 103 | font-weight: 400; 104 | font-size: 10px; 105 | text-align: center; 106 | display: inline-block; 107 | text-transform: lowercase; 108 | margin: auto; 109 | top: 24px; 110 | overflow: hidden; 111 | white-space: nowrap; 112 | text-overflow: ellipsis; 113 | width: 100%; 114 | } 115 | 116 | .info-container { 117 | margin: 0 auto; 118 | text-align: center; 119 | max-width: 450px; 120 | } 121 | .hashtag, .info, .author { 122 | margin: 0 20px; 123 | display: block; 124 | text-align: left; 125 | } 126 | 127 | .hashtag { 128 | font-size: 24px; 129 | line-height: 32px; 130 | } 131 | 132 | .info { 133 | font-size: 12px; 134 | margin-bottom: 20px; 135 | line-height: 18px; 136 | position: relative; 137 | } 138 | .info:after { 139 | position: absolute; 140 | bottom: -13px; 141 | left: 0; 142 | height: 3px; 143 | width: 20px; 144 | background: #D8D8D8; 145 | content: ' '; 146 | } 147 | 148 | .bullet { 149 | color: #D8D8D8; 150 | } 151 | 152 | .author, a { 153 | font-size: 12px; 154 | text-decoration: none; 155 | color: #000; 156 | } 157 | 158 | a { 159 | color: #48a0cd; 160 | position: relative; 161 | } 162 | 163 | .no-touch a:after { 164 | content: ' '; 165 | position: absolute; 166 | bottom: -4px; 167 | left: 0; 168 | width: 100%; 169 | height: 0px; 170 | background: currentColor; 171 | display: inline-block; 172 | } 173 | .no-touch a:hover:after { 174 | height: 3px; 175 | bottom: -1px; 176 | } 177 | 178 | .no-touch a:after, 179 | .no-touch a:hover:after { 180 | -webkit-transition: all 0.05s ease-out; 181 | -moz-transition: all 0.05s ease-out; 182 | -ms-transition: all 0.05s ease-out; 183 | -o-transition: all 0.05s ease-out; 184 | transition: all 0.05s ease-out; 185 | } 186 | 187 | .icons { 188 | position: absolute; 189 | display: inline-block; 190 | bottom: 6px; 191 | right: 9px; 192 | opacity: 0.25; 193 | } 194 | .icon-mobile, .icon-audio { 195 | width: 14px; 196 | } -------------------------------------------------------------------------------- /src/grid/palette.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var getPixels = require('get-pixels') 4 | var getPalette = require('get-rgba-palette') 5 | var mapLimit = require('map-limit') 6 | var thumbs = path.resolve(process.cwd(), process.argv[2]) 7 | var sortByName = require('./sort-files') 8 | 9 | var files = fs.readdirSync(thumbs).filter(function (f) { 10 | return /\.(jpe?g|png)$/i.test(f) 11 | }) 12 | files.sort(sortByName) 13 | console.error(files) 14 | 15 | mapLimit(files, 5, function (item, next) { 16 | var file = path.resolve(thumbs, item) 17 | getPixels(file, function (err, pixels) { 18 | if (err) return next(err) 19 | var data = pixels.data 20 | var palette = getPalette(data, 3) 21 | next(null, palette) 22 | }) 23 | }, function (err, results) { 24 | if (err) throw err 25 | // console.error(results) 26 | // var dict = results.reduce(function (dict, item) { 27 | // dict[item.file] = item.palette 28 | // return dict 29 | // }, {}) 30 | console.log(JSON.stringify(results)) 31 | }) 32 | 33 | -------------------------------------------------------------------------------- /src/grid/sort-files.js: -------------------------------------------------------------------------------- 1 | module.exports = function sortByName (a, b) { 2 | var aNum = parseInt(/\d+/.exec(a)[0], 10) 3 | var bNum = parseInt(/\d+/.exec(b)[0], 10) 4 | return aNum - bNum 5 | } -------------------------------------------------------------------------------- /src/is-low-end.js: -------------------------------------------------------------------------------- 1 | const getGL = require('webgl-context') 2 | module.exports = function isLowEnd () { 3 | const gl = getGL() 4 | const maxSize = gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : 0 5 | if (maxSize <= 4096 * 2) { 6 | return true // stupid "Low End" Mobile/FF test 7 | } 8 | return false 9 | } -------------------------------------------------------------------------------- /src/is-mobile.js: -------------------------------------------------------------------------------- 1 | module.exports = function isMobile () { 2 | // dumb mobile test 3 | return /(iPad|iPhone|Android)/i.test(navigator.userAgent) 4 | } -------------------------------------------------------------------------------- /src/old-10.js: -------------------------------------------------------------------------------- 1 | global.THREE = require('three') 2 | var createOrbitViewer = require('three-orbit-viewer')(THREE) 3 | var createText = require('three-bmfont-text') 4 | var loadBMFont = require('load-bmfont') 5 | var fatal = require('./fatal-error')() 6 | var parallel = require('run-parallel') 7 | var once = require('once') 8 | var createSDF = require('./shaders/sdf') 9 | 10 | parallel([ 11 | (next) => loadBMFont('assets/KelsonSans.fnt', next), 12 | (next) => { 13 | next = once(next) 14 | THREE.ImageUtils.loadTexture('assets/KelsonSans.png', undefined, 15 | (tex) => next(null, tex), 16 | (err) => next(err)) 17 | } 18 | ], (err, [ font, texture ]) => { 19 | if (err) return fatal(err) 20 | start(font, texture) 21 | }) 22 | 23 | function start(font, texture) { 24 | var app = createOrbitViewer({ 25 | clearColor: 'rgb(80, 80, 80)', 26 | clearAlpha: 1.0, 27 | fov: 65, 28 | position: new THREE.Vector3() 29 | }) 30 | 31 | app.camera = new THREE.OrthographicCamera() 32 | app.camera.left = 0 33 | app.camera.top = 0 34 | app.camera.near = -100 35 | app.camera.far = 100 36 | 37 | var geom = createText({ 38 | text: 'hello world', 39 | font: font, 40 | align: 'left', 41 | width: 700, 42 | flipY: texture.flipY 43 | }) 44 | 45 | var material = new THREE.RawShaderMaterial(createSDF({ 46 | map: texture, 47 | transparent: true, 48 | side: THREE.DoubleSide, 49 | depthTest: false, 50 | color: 'rgb(230, 230, 230)' 51 | })) 52 | 53 | var layout = geom.layout 54 | var text = new THREE.Mesh(geom, material) 55 | var padding = 20 56 | text.position.set(padding, layout.height + padding, 0) 57 | 58 | var textAnchor = new THREE.Object3D() 59 | textAnchor.add(text) 60 | textAnchor.scale.multiplyScalar(5) 61 | textAnchor.scale.multiplyScalar(1/(window.devicePixelRatio||1)) 62 | app.scene.add(textAnchor) 63 | 64 | //update orthographic 65 | app.on('tick', function() { 66 | //update camera 67 | var width = app.engine.width 68 | var height = app.engine.height 69 | app.camera.right = width 70 | app.camera.bottom = height 71 | app.camera.updateProjectionMatrix() 72 | }) 73 | } -------------------------------------------------------------------------------- /src/shaders/14-post.frag: -------------------------------------------------------------------------------- 1 | #define LUT_FLIP_Y 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D tDiffuse; 5 | uniform vec2 resolution; 6 | uniform float iGlobalTime; 7 | uniform sampler2D tLookup; 8 | 9 | vec3 tex(vec2 uv); 10 | 11 | #pragma glslify: blur = require('glsl-hash-blur', sample=tex, iterations=10) 12 | #pragma glslify: lut = require('glsl-lut') 13 | 14 | vec3 tex(vec2 uv) { 15 | return texture2D(tDiffuse, uv).rgb; 16 | } 17 | 18 | void main () { 19 | float aspect = resolution.x / resolution.y; 20 | 21 | //jitter the noise but not every frame 22 | float tick = floor(fract(iGlobalTime)*20.0); 23 | float jitter = mod(tick * 382.0231, 21.321); 24 | 25 | vec3 blurred = vec3(0.0); 26 | blurred += 0.6 * blur(vUv, 0.3, 1.0 / aspect, jitter); 27 | 28 | gl_FragColor = texture2D(tDiffuse, vUv); 29 | gl_FragColor.rgb += blurred; 30 | gl_FragColor.rgb = lut(gl_FragColor, tLookup).rgb; 31 | gl_FragColor.a = 1.0; 32 | } -------------------------------------------------------------------------------- /src/shaders/14-post.js: -------------------------------------------------------------------------------- 1 | const glslify = require('glslify') 2 | module.exports = { 3 | uniforms: { 4 | tDiffuse: { type: 't', value: null }, 5 | tLookup: { type: 't', value: null }, 6 | iGlobalTime: { type: 'f', value: 0 }, 7 | resolution: { type: 'v2', value: new THREE.Vector2() } 8 | }, 9 | vertexShader: glslify(__dirname + '/14-post.vert'), 10 | fragmentShader: glslify(__dirname + '/14-post.frag') 11 | } 12 | -------------------------------------------------------------------------------- /src/shaders/14-post.vert: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | void main() { 3 | vUv = uv; 4 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 5 | } 6 | -------------------------------------------------------------------------------- /src/shaders/14.frag: -------------------------------------------------------------------------------- 1 | #extension GL_OES_standard_derivatives : enable 2 | 3 | precision highp float; 4 | uniform vec3 color; 5 | uniform float opacity; 6 | uniform float iGlobalTime; 7 | varying vec2 vUv; 8 | varying vec3 vPos; 9 | uniform int mode; 10 | 11 | #pragma glslify: hsl2rgb = require('glsl-hsl2rgb') 12 | #pragma glslify: aastep = require('glsl-aastep') 13 | 14 | #pragma glslify: noise = require('glsl-noise/simplex/3d') 15 | 16 | void main() { 17 | float dst = 0.0; 18 | float timeOff = sin(iGlobalTime * 0.01) * 80.0; 19 | 20 | // float yPos 21 | vec3 samplePos = vec3(vUv.x * 1.0, vUv.y * 20.0 + timeOff, timeOff * 0.15); 22 | 23 | #ifdef DESKTOP 24 | samplePos.y += 3.5 * noise(vec3(vPos.xy * 1.0, timeOff * 0.05)); 25 | samplePos.y += 0.05 * noise(vec3(vPos.xy * 5.0, timeOff * 0.5)); 26 | samplePos.y += 0.5 * noise(vec3(vPos.xy * 7.0, timeOff)); 27 | #endif 28 | 29 | float absOff = 1.0; 30 | if (mode == 1) { 31 | absOff = 0.5; 32 | } 33 | 34 | float cDist = length(vPos.xy) - 0.5; 35 | cDist = abs(cDist - absOff); 36 | float circle = smoothstep(0.0, 1.5, cDist); 37 | samplePos.xy *= circle; 38 | 39 | dst += noise(samplePos); 40 | 41 | float smooth = dst; 42 | dst = aastep(0.25, dst); 43 | 44 | float hue = mix(0.4, 0.2, vUv.x); 45 | float light = mix(0.4, 0.6, vUv.y); 46 | float hueOff = sin(timeOff * 0.5) * 0.5 + 0.5; 47 | hueOff += (hue * 2.0 - 1.0) * 0.4; 48 | 49 | gl_FragColor.rgb = hsl2rgb(hueOff, 0.75, light); 50 | gl_FragColor.a = dst; 51 | gl_FragColor.a *= opacity; 52 | } -------------------------------------------------------------------------------- /src/shaders/14.vert: -------------------------------------------------------------------------------- 1 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 2 | 3 | attribute vec3 position; 4 | attribute vec2 uv; 5 | uniform mat4 projectionMatrix; 6 | uniform mat4 modelViewMatrix; 7 | uniform mat4 modelMatrix; 8 | uniform float iGlobalTime; 9 | varying vec2 vUv; 10 | varying vec3 vPos; 11 | 12 | void main() { 13 | vec4 posW = vec4(position, 1.0); 14 | 15 | float theta = 0.1 * noise(vec4(position.yyy * 10.0, iGlobalTime * 0.5)); 16 | mat3 rotMat = mat3( 17 | vec3(cos(theta), 0.0, sin(theta)), 18 | vec3(0.0, 1.0, 0.0), 19 | vec3(-sin(theta), 0.0, cos(theta)) 20 | ); 21 | 22 | posW.xyz = rotMat * posW.xyz; 23 | vPos = vec3(modelMatrix * posW); 24 | vUv = uv; 25 | gl_Position = projectionMatrix * 26 | modelViewMatrix * 27 | posW; 28 | } -------------------------------------------------------------------------------- /src/shaders/15-post.frag: -------------------------------------------------------------------------------- 1 | #define LUT_FLIP_Y 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D tDiffuse; 5 | uniform sampler2D tDust; 6 | uniform vec2 resolution; 7 | uniform vec2 dustResolution; 8 | uniform float iGlobalTime; 9 | uniform sampler2D tLookup; 10 | 11 | vec3 tex(vec2 uv); 12 | 13 | #pragma glslify: blur = require('glsl-hash-blur', sample=tex, iterations=20) 14 | #pragma glslify: lut = require('glsl-lut') 15 | #pragma glslify: luma = require('glsl-luma') 16 | 17 | vec3 tex(vec2 uv) { 18 | return texture2D(tDiffuse, uv).rgb; 19 | } 20 | 21 | vec4 textureBackground(sampler2D texture, vec2 uv, vec2 resolution, vec2 texResolution) { 22 | float tAspect = texResolution.x / texResolution.y; 23 | float pwidth = resolution.x; 24 | float pheight = resolution.y; 25 | float pAspect = resolution.x / resolution.y; 26 | 27 | float width = 0.0; 28 | float height = 0.0; 29 | if (tAspect > pAspect) { 30 | height = pheight; 31 | width = height * tAspect; 32 | } else { 33 | width = pwidth; 34 | height = width / tAspect; 35 | } 36 | float x = (pwidth - width) / 2.0; 37 | float y = (pheight - height) / 2.0; 38 | vec2 nUv = uv; 39 | nUv -= vec2(x, y) / resolution; 40 | nUv /= vec2(width, height) / resolution; 41 | return texture2D(texture, nUv); 42 | } 43 | 44 | void main () { 45 | float aspect = resolution.x / resolution.y; 46 | 47 | //jitter the noise but not every frame 48 | float tick = floor(fract(iGlobalTime)*20.0); 49 | float jitter = mod(tick * 382.0231, 21.321); 50 | 51 | vec3 blurred = vec3(0.0); 52 | blurred += 0.3 * blur(vUv, 0.1, 1.0 / aspect, jitter); 53 | 54 | gl_FragColor = texture2D(tDiffuse, vUv); 55 | 56 | gl_FragColor.rgb += blurred; 57 | 58 | vec2 vigUV = vUv - 0.5; 59 | vigUV.x *= aspect; 60 | float vig = length(vigUV); 61 | vig = smoothstep(1.6, 0.2, vig); 62 | gl_FragColor *= vig; 63 | 64 | 65 | float L = luma(blurred.rgb); 66 | L = smoothstep(0.015, 0.1, L); 67 | 68 | vec4 dust = textureBackground(tDust, vUv, resolution, dustResolution); 69 | vec3 dustyColor = gl_FragColor.rgb + dust.rgb; 70 | gl_FragColor.rgb = mix(gl_FragColor.rgb, dustyColor, L * 0.75); 71 | 72 | gl_FragColor.rgb = lut(gl_FragColor, tLookup).rgb; 73 | gl_FragColor.a = 1.0; 74 | 75 | } -------------------------------------------------------------------------------- /src/shaders/15-post.js: -------------------------------------------------------------------------------- 1 | const glslify = require('glslify') 2 | module.exports = { 3 | uniforms: { 4 | tDiffuse: { type: 't', value: null }, 5 | tDust: { type: 't', value: null }, 6 | tLookup: { type: 't', value: null }, 7 | iGlobalTime: { type: 'f', value: 0 }, 8 | dustResolution: { type: 'v2', value: new THREE.Vector2() }, 9 | resolution: { type: 'v2', value: new THREE.Vector2() } 10 | }, 11 | vertexShader: glslify(__dirname + '/14-post.vert'), 12 | fragmentShader: glslify(__dirname + '/15-post.frag') 13 | } 14 | -------------------------------------------------------------------------------- /src/shaders/15.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec3 color; 3 | uniform float opacity; 4 | uniform float iGlobalTime; 5 | 6 | void main() { 7 | gl_FragColor.rgb = color; 8 | gl_FragColor.a = opacity; 9 | } -------------------------------------------------------------------------------- /src/shaders/15.vert: -------------------------------------------------------------------------------- 1 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 2 | 3 | attribute vec3 position; 4 | attribute vec3 direction; 5 | uniform mat4 projectionMatrix; 6 | uniform mat4 modelViewMatrix; 7 | uniform mat4 modelMatrix; 8 | uniform vec3 mouse; 9 | uniform float iGlobalTime; 10 | varying vec3 vPos; 11 | 12 | void main() { 13 | vec4 posW = vec4(position, 1.0); 14 | 15 | vec3 offset = vec3(0.0); 16 | offset += direction * noise(vec4(posW.xyz * 1.0, iGlobalTime)) * 0.5; 17 | posW.xyz = posW.xyz + offset; 18 | 19 | gl_Position = projectionMatrix * 20 | modelViewMatrix * 21 | posW; 22 | } -------------------------------------------------------------------------------- /src/shaders/17.frag: -------------------------------------------------------------------------------- 1 | #extension GL_OES_standard_derivatives : enable 2 | 3 | precision highp float; 4 | uniform vec3 color; 5 | uniform float opacity; 6 | uniform float radius; 7 | varying vec3 vPos; 8 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 9 | #pragma glslify: aastep = require('glsl-aastep') 10 | 11 | void main() { 12 | float dist = length(vPos) / radius; 13 | float fade = smoothstep(0.1, 0.9, dist); 14 | 15 | float steps = 4.0; 16 | float pattern = mod(dist, 1.0 / steps) * steps; //smoothstep(0.51, 0.49, ); 17 | pattern = smoothstep(0.8, 0.0, abs(pattern - 0.5)); 18 | 19 | gl_FragColor.rgb = mix(vec3(#5af24f), vec3(#e72b4a), pattern); 20 | gl_FragColor.a = opacity * fade; 21 | gl_FragColor.rgb *= gl_FragColor.a; 22 | } -------------------------------------------------------------------------------- /src/shaders/17.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | uniform mat4 projectionMatrix; 3 | uniform mat4 modelViewMatrix; 4 | uniform mat4 modelMatrix; 5 | varying vec3 vPos; 6 | 7 | void main() { 8 | vec4 posW = vec4(position, 1.0); 9 | vPos = posW.xyz; 10 | gl_Position = projectionMatrix * 11 | modelViewMatrix * 12 | posW; 13 | } -------------------------------------------------------------------------------- /src/shaders/21-line.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform sampler2D brushTexture; 4 | uniform vec3 color; 5 | uniform float opacity; 6 | uniform bool useHue; 7 | uniform float iGlobalTime; 8 | varying float vAngle; 9 | varying vec2 uvCoords; 10 | 11 | #define PI 3.14 12 | #pragma glslify: hsl2rgb = require('glsl-hsl2rgb') 13 | 14 | void main() { 15 | vec3 tCol = color; 16 | if (useHue) { 17 | float sat = 0.7; 18 | float light = 0.6; 19 | 20 | float rainbow = sin(vAngle * 1.0) * 0.5 + 0.5; 21 | float hue = 0.0; 22 | hue += mix(0.5, 0.9, rainbow); 23 | tCol = hsl2rgb(vec3(hue, sat, light)); 24 | } 25 | 26 | // vec2 vUv = vec2(uvCoords.x, 1.0 - abs(uvCoords.y)); 27 | // vec4 brush = texture2D(brushTexture, vUv); 28 | 29 | gl_FragColor = vec4(tCol, opacity); 30 | gl_FragColor.rgb *= gl_FragColor.a; 31 | } -------------------------------------------------------------------------------- /src/shaders/21-line.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute float direction; 3 | attribute vec3 next; 4 | attribute vec3 previous; 5 | 6 | uniform sampler2D audioTexture; 7 | uniform mat4 projection; 8 | uniform mat4 model; 9 | uniform mat4 view; 10 | uniform float aspect; 11 | uniform float radius; 12 | uniform float index; 13 | 14 | uniform float thickness; 15 | uniform float iGlobalTime; 16 | uniform int miter; 17 | 18 | varying float vAngle; 19 | varying vec2 uvCoords; 20 | 21 | #define HAS_VERTEX_SAMPLER 22 | #define PI 3.14 23 | 24 | #pragma glslify: analyse = require('gl-audio-analyser') 25 | #pragma glslify: noise4d = require('glsl-noise/simplex/4d') 26 | #pragma glslify: noise3d = require('glsl-noise/simplex/3d') 27 | 28 | vec4 line3D (vec3 positionOffset, float computedThickness) { 29 | vec2 aspectVec = vec2(aspect, 1.0); 30 | mat4 projViewModel = projection * view * model; 31 | vec4 previousProjected = projViewModel * vec4(previous + positionOffset, 1.0); 32 | vec4 currentProjected = projViewModel * vec4(position + positionOffset, 1.0); 33 | vec4 nextProjected = projViewModel * vec4(next + positionOffset, 1.0); 34 | 35 | //get 2D screen space with W divide and aspect correction 36 | vec2 currentScreen = currentProjected.xy / currentProjected.w * aspectVec; 37 | vec2 previousScreen = previousProjected.xy / previousProjected.w * aspectVec; 38 | vec2 nextScreen = nextProjected.xy / nextProjected.w * aspectVec; 39 | 40 | float len = computedThickness; 41 | float orientation = direction; 42 | 43 | //starting point uses (next - current) 44 | vec2 dir = vec2(0.0); 45 | if (currentScreen == previousScreen) { 46 | dir = normalize(nextScreen - currentScreen); 47 | } 48 | //ending point uses (current - previous) 49 | else if (currentScreen == nextScreen) { 50 | dir = normalize(currentScreen - previousScreen); 51 | } 52 | //somewhere in middle, needs a join 53 | else { 54 | //get directions from (C - B) and (B - A) 55 | vec2 dirA = normalize((currentScreen - previousScreen)); 56 | if (miter == 1) { 57 | vec2 dirB = normalize((nextScreen - currentScreen)); 58 | //now compute the miter join normal and length 59 | vec2 tangent = normalize(dirA + dirB); 60 | vec2 perp = vec2(-dirA.y, dirA.x); 61 | vec2 miter = vec2(-tangent.y, tangent.x); 62 | dir = tangent; 63 | len = computedThickness / dot(miter, perp); 64 | } else { 65 | dir = dirA; 66 | } 67 | } 68 | vec2 normal = vec2(-dir.y, dir.x); 69 | normal *= len/2.0; 70 | normal.x /= aspect; 71 | 72 | vec4 offset = vec4(normal * orientation, 0.0, 1.0); 73 | return currentProjected + offset; 74 | } 75 | 76 | float turb (float angle, float alpha, float scale, float offset) { 77 | return alpha * noise3d(vec3(position.x * scale, angle, offset)); 78 | } 79 | 80 | void main() { 81 | #ifdef HAS_VERTEX_SAMPLER 82 | float frequencies = analyse(audioTexture, position.x * 0.5 + 0.5); 83 | #endif 84 | 85 | float pinch = smoothstep(0.0, 0.5, 1.0 - abs(position.x)); 86 | float computedThickness = thickness; 87 | computedThickness *= pinch; 88 | 89 | vec3 offset = vec3(0.0); 90 | 91 | // we will wrap the lines around like a finger trap 92 | float angleOffset = index * PI; // separate each line a bit 93 | float twists = 0.5; // twisting factor 94 | float radialOffset = 2.0 * PI * position.x; 95 | 96 | #ifdef HAS_VERTEX_SAMPLER 97 | angleOffset *= mix(0.5, 1.5, frequencies); 98 | #endif 99 | 100 | float angle = radialOffset * twists + angleOffset; 101 | angle += iGlobalTime; 102 | 103 | 104 | float computedRadius = radius; 105 | #ifdef HAS_VERTEX_SAMPLER 106 | computedRadius += turb(angle, frequencies * 0.5, 1.0, iGlobalTime * 0.2); 107 | #endif 108 | computedRadius += turb(angle, 0.1, 6.0, iGlobalTime * 0.25); 109 | computedRadius += turb(angle, 0.1, 2.5, iGlobalTime * 0.5); 110 | computedRadius *= pinch; 111 | #ifdef HAS_VERTEX_SAMPLER 112 | computedRadius *= mix(0.65, 1.0, frequencies); 113 | #endif 114 | 115 | vAngle = angle; 116 | offset.y = cos(angle) * computedRadius; 117 | offset.z = sin(angle) * computedRadius; 118 | 119 | uvCoords = vec2(position.x * 0.5 + 0.5, sign(direction)); 120 | gl_Position = line3D(offset, computedThickness); 121 | gl_PointSize = 1.0; 122 | } -------------------------------------------------------------------------------- /src/shaders/23.frag: -------------------------------------------------------------------------------- 1 | #extension GL_OES_standard_derivatives : enable 2 | precision highp float; 3 | #define SQRT2_2 0.70710678118654757 4 | 5 | uniform float opacity; 6 | uniform vec3 color; 7 | uniform sampler2D map; 8 | uniform vec2 iResolution; 9 | uniform float iGlobalTime; 10 | uniform vec2 textSize; 11 | varying vec2 vUv; 12 | varying vec3 vPos; 13 | varying vec2 vCenterUV; 14 | 15 | #pragma glslify: noise = require('glsl-noise/simplex/3d') 16 | #pragma glslify: noise2d = require('glsl-noise/simplex/2d') 17 | #pragma glslify: aastep = require('glsl-aastep') 18 | #pragma glslify: blend = require('./src-over') 19 | 20 | #pragma glslify: hsl2rgb = require('glsl-hsl2rgb') 21 | 22 | // vec3 render (float sdf) { 23 | // float hue = noise(vec3(vUv.x * 0.0, sdf * 5.0, 0.0)); 24 | // return hsl2rgb(vec3(hue, 0.5, 0.5)); 25 | // } 26 | 27 | void main() { 28 | vec4 texColor = texture2D(map, vUv); 29 | float sdf = texColor.a; 30 | 31 | vec2 pos = vec2(vPos.xy / textSize); 32 | pos.y *= -1.0; 33 | pos -= 0.5; 34 | pos.x *= abs(textSize.x / textSize.y); 35 | 36 | float timeOff = iGlobalTime * 0.2; 37 | float alpha = 0.0; 38 | alpha += aastep(0.5, sdf + 0.25 * noise(vec3(vUv * 1150.0, timeOff))); 39 | alpha -= aastep(0.5, sdf + 0.25 * noise(vec3(vUv * 50.0, timeOff))); 40 | alpha += aastep(0.6, sdf + 0.0 * noise(vec3(vUv * 500.0, timeOff))); 41 | 42 | float mask = length(pos); 43 | mask = aastep(0.5, mask); 44 | alpha -= 0.75 * mask; 45 | 46 | vec3 colorA = vec3(#000); 47 | vec3 colorB = vec3(#ff0000); 48 | vec4 baseA = vec4(colorA, opacity * alpha); 49 | gl_FragColor = baseA; 50 | } -------------------------------------------------------------------------------- /src/shaders/23.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute vec2 uv; 3 | attribute vec3 direction; 4 | uniform mat4 projectionMatrix; 5 | uniform vec2 textSize; 6 | uniform mat4 modelViewMatrix; 7 | uniform mat4 modelMatrix; 8 | varying vec2 vUv; 9 | varying vec3 vPos; 10 | varying vec2 vCenterUV; 11 | 12 | void main() { 13 | vec4 posW = vec4(position, 1.0); 14 | vPos = posW.xyz; 15 | vUv = uv; 16 | 17 | vec4 modelPos = modelMatrix * posW; 18 | vec3 origin = vec3(0.0); 19 | vCenterUV = normalize(modelPos.xyz - origin.xyz).xy; 20 | gl_Position = projectionMatrix * 21 | modelViewMatrix * 22 | posW; 23 | } -------------------------------------------------------------------------------- /src/shaders/27.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec3 color; 3 | uniform float opacity; 4 | 5 | void main() { 6 | gl_FragColor = vec4(color, opacity); 7 | } -------------------------------------------------------------------------------- /src/shaders/27.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | uniform mat4 projectionMatrix; 3 | uniform mat4 modelViewMatrix; 4 | uniform mat4 modelMatrix; 5 | uniform float iGlobalTime; 6 | 7 | uniform float thickness; 8 | attribute float lineMiter; 9 | attribute vec2 lineNormal; 10 | 11 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 12 | 13 | void main() { 14 | float n = noise(vec4(position.xyz, iGlobalTime)); 15 | float computedThickness = thickness * n; 16 | vec2 normal = clamp(lineNormal, vec2(-1.0), vec2(1.0)); 17 | float miter = clamp(lineMiter, -1.0, 1.0); 18 | vec2 pointPos = position.xy + vec2(lineNormal * computedThickness / 2.0 * miter); 19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(pointPos, position.z, 1.0); 20 | } -------------------------------------------------------------------------------- /src/shaders/28.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #pragma glslify: envMapEquirect = require('./envmap-equirect.glsl') 4 | #pragma glslify: toLinear = require('glsl-gamma/in') 5 | #pragma glslify: toGamma = require('glsl-gamma/out') 6 | #pragma glslify: tonemap = require('./tonemap-filmic') 7 | 8 | uniform vec3 color; 9 | uniform float opacity; 10 | uniform sampler2D map; 11 | varying vec2 vUv; 12 | varying mat4 vInverseViewMatrix; 13 | 14 | varying vec3 ecPosition; 15 | varying vec3 ecNormal; 16 | 17 | //White balance middle grey we are targetting for a good scene exposure 18 | //https://en.wikipedia.org/wiki/Middle_gray 19 | const float MIDDLE_GREY = 0.18; 20 | 21 | vec3 fixSeams(vec3 vec, float mipmapIndex) { 22 | float scale = 1.0 - exp2(mipmapIndex) / 256.0; 23 | float M = max(max(abs(vec.x), abs(vec.y)), abs(vec.z)); 24 | if (abs(vec.x) != M) vec.x *= scale; 25 | if (abs(vec.y) != M) vec.y *= scale; 26 | if (abs(vec.z) != M) vec.z *= scale; 27 | return vec; 28 | } 29 | 30 | vec3 fixSeamsStatic(vec3 vec, float invRecMipSize) { 31 | float scale = invRecMipSize; 32 | float M = max(max(abs(vec.x), abs(vec.y)), abs(vec.z)); 33 | if (abs(vec.x) != M) vec.x *= scale; 34 | if (abs(vec.y) != M) vec.y *= scale; 35 | if (abs(vec.z) != M) vec.z *= scale; 36 | return vec; 37 | } 38 | 39 | 40 | /* 41 | * Get an exposure using the Standard Output Sensitivity method. 42 | * Accepts an additional parameter of the target middle grey. 43 | */ 44 | float getStandardOutputBasedExposure(float aperture, 45 | float shutterSpeed, 46 | float iso) { 47 | float q = 0.65; 48 | //float l_avg = (1000.0f / 65.0f) * sqrt(aperture) / (iso * shutterSpeed); 49 | float l_avg = (1.0 / q) * sqrt(aperture) / (iso * shutterSpeed); 50 | //float l_avg = sqrt(aperture) / (iso * shutterSpeed); 51 | return MIDDLE_GREY / l_avg; 52 | } 53 | 54 | void main() { 55 | vec3 ecEyeDir = normalize(-ecPosition); 56 | vec3 wcEyeDir = vec3(vInverseViewMatrix * vec4(ecEyeDir, 0.0)); 57 | vec3 wcNormal = vec3(vInverseViewMatrix * vec4(ecNormal, 0.0)); 58 | 59 | vec3 reflectionWorld = normalize(reflect(-wcEyeDir, wcNormal)); 60 | vec2 envUV = envMapEquirect(reflectionWorld); 61 | vec3 outCol = texture2D(map, envUV, 4.0).rgb; 62 | outCol = toLinear(outCol); 63 | outCol.rgb *= getStandardOutputBasedExposure(16.0, 0.5, 100.0); 64 | outCol.rgb = tonemap(outCol.rgb); 65 | outCol = toGamma(outCol); 66 | gl_FragColor = vec4(outCol * color, opacity); 67 | } -------------------------------------------------------------------------------- /src/shaders/28.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute vec2 uv; 3 | attribute vec3 normal; 4 | 5 | uniform mat4 projectionMatrix; 6 | uniform mat4 modelViewMatrix; 7 | uniform mat4 modelMatrix; 8 | uniform mat3 normalMatrix; 9 | uniform mat4 viewMatrix; 10 | varying vec2 vUv; 11 | varying mat4 vInverseViewMatrix; 12 | 13 | #pragma glslify: inverse = require('glsl-inverse') 14 | 15 | varying vec3 ecPosition; 16 | varying vec3 ecNormal; 17 | 18 | void main() { 19 | vec4 posW = vec4(position, 1.0); 20 | ecPosition = vec3(viewMatrix * modelMatrix * posW); 21 | ecNormal = normalMatrix * normal; 22 | 23 | vInverseViewMatrix = inverse(viewMatrix); 24 | vUv = uv; 25 | gl_Position = projectionMatrix * 26 | modelViewMatrix * 27 | vec4(position, 1.0); 28 | } -------------------------------------------------------------------------------- /src/shaders/29.frag: -------------------------------------------------------------------------------- 1 | #extension GL_OES_standard_derivatives : enable 2 | precision highp float; 3 | 4 | #pragma glslify: envMapEquirect = require('./envmap-equirect.glsl') 5 | #pragma glslify: toLinear = require('glsl-gamma/in') 6 | #pragma glslify: toGamma = require('glsl-gamma/out') 7 | #pragma glslify: tonemap = require('./tonemap-filmic') 8 | #pragma glslify: rgbmDecode = require('./rgbm-decode') 9 | #pragma glslify: perturb = require('glsl-perturb-normal') 10 | 11 | uniform vec3 color; 12 | uniform float opacity; 13 | uniform sampler2D map; 14 | uniform sampler2D diffuseMap; 15 | uniform sampler2D normalMap; 16 | varying vec2 vUv; 17 | varying mat4 vInverseViewMatrix; 18 | 19 | varying vec3 ecPosition; 20 | varying vec3 ecNormal; 21 | 22 | //White balance middle grey we are targetting for a good scene exposure 23 | //https://en.wikipedia.org/wiki/Middle_gray 24 | const float MIDDLE_GREY = 0.18; 25 | 26 | /* 27 | * Get an exposure using the Standard Output Sensitivity method. 28 | * Accepts an additional parameter of the target middle grey. 29 | */ 30 | float getStandardOutputBasedExposure(float aperture, 31 | float shutterSpeed, 32 | float iso) { 33 | float q = 0.65; 34 | //float l_avg = (1000.0f / 65.0f) * sqrt(aperture) / (iso * shutterSpeed); 35 | float l_avg = (1.0 / q) * sqrt(aperture) / (iso * shutterSpeed); 36 | //float l_avg = sqrt(aperture) / (iso * shutterSpeed); 37 | return MIDDLE_GREY / l_avg; 38 | } 39 | 40 | vec4 textureEnv (sampler2D sampler, vec3 dir) { 41 | vec2 envUV = envMapEquirect(dir); 42 | return vec4(toLinear(rgbmDecode(texture2D(sampler, envUV))), 1.0); 43 | } 44 | 45 | void main() { 46 | // gl_FragColor = vec4(1.0); 47 | vec3 ecEyeDir = normalize(-ecPosition); 48 | vec3 wcEyeDir = vec3(vInverseViewMatrix * vec4(ecEyeDir, 0.0)); 49 | vec3 wcNormal = vec3(vInverseViewMatrix * vec4(ecNormal, 0.0)); 50 | 51 | vec3 N = normalize(wcNormal); 52 | vec3 reflectionWorld = normalize(reflect(-wcEyeDir, wcNormal)); 53 | vec3 normalRGB = texture2D(normalMap, vUv * 1.5).rgb; 54 | normalRGB = normalRGB * 2.0 - 1.0; 55 | normalRGB = mix(vec3(0.0, 0.0, 1.0), normalRGB, 0.75); 56 | 57 | //perturb the normal 58 | vec3 pNormal = perturb(normalRGB, reflectionWorld, wcEyeDir, vUv); 59 | 60 | vec3 reflectCol = textureEnv(map, pNormal).rgb; 61 | vec3 diffuseCol = textureEnv(diffuseMap, pNormal).rgb; 62 | 63 | // this is not at all accurate, just finding stuff that looks pleasing. 64 | vec3 outCol = diffuseCol + reflectCol; 65 | outCol.rgb *= getStandardOutputBasedExposure(16.0, 0.5, 50.0); 66 | outCol.rgb = tonemap(outCol.rgb); 67 | outCol = toGamma(outCol); 68 | gl_FragColor = vec4(outCol * color, opacity); 69 | } -------------------------------------------------------------------------------- /src/shaders/29.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute vec2 uv; 3 | attribute vec3 normal; 4 | attribute vec3 randomDirection; 5 | 6 | uniform mat4 projectionMatrix; 7 | uniform mat4 modelViewMatrix; 8 | uniform mat4 modelMatrix; 9 | uniform mat3 normalMatrix; 10 | uniform mat4 viewMatrix; 11 | uniform float iGlobalTime; 12 | varying vec2 vUv; 13 | varying mat4 vInverseViewMatrix; 14 | 15 | #pragma glslify: inverseMat = require('glsl-inverse') 16 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 17 | #pragma glslify: ease = require('glsl-easings/quartic-in-out') 18 | 19 | varying vec3 ecPosition; 20 | varying vec3 ecNormal; 21 | 22 | void main() { 23 | vec4 posW = vec4(position, 1.0); 24 | 25 | float anim = ease(sin(iGlobalTime * 2.0) * 0.5 + 0.5); 26 | float dir = 0.0; 27 | 28 | float spin = length(position.xz); 29 | dir += anim * noise(vec4(position.xyz * 2.0 * spin, anim)); 30 | posW.xyz += randomDirection * dir; 31 | 32 | ecPosition = vec3(viewMatrix * modelMatrix * posW); 33 | ecNormal = normalMatrix * normal; 34 | 35 | vInverseViewMatrix = inverseMat(viewMatrix); 36 | vUv = uv; 37 | gl_Position = projectionMatrix * 38 | modelViewMatrix * 39 | posW; 40 | } -------------------------------------------------------------------------------- /src/shaders/3.frag: -------------------------------------------------------------------------------- 1 | uniform vec3 color; 2 | uniform float opacity; 3 | void main() { 4 | gl_FragColor = vec4(color, opacity); 5 | } -------------------------------------------------------------------------------- /src/shaders/3.vert: -------------------------------------------------------------------------------- 1 | 2 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 3 | 4 | varying vec2 vUv; 5 | uniform int ring; 6 | attribute float freqLow; 7 | attribute float freqMid; 8 | attribute float freqHigh; 9 | 10 | uniform float iGlobalTime; 11 | void main() { 12 | vUv = uv; 13 | 14 | float freq = 0.0; 15 | if (ring == 0) freq = freqLow; 16 | else if (ring == 1) freq = freqMid; 17 | else if (ring == 2) freq = freqHigh; 18 | 19 | float angle = atan(position.y, position.x) * 1024.0 + iGlobalTime; 20 | vec3 offset = position.xyz; 21 | float movement = sin(cos(iGlobalTime)); 22 | offset.z = noise(vec4(position.xy * freq, angle, movement)) * 1.25; 23 | gl_Position = projectionMatrix * 24 | modelViewMatrix * 25 | vec4(offset, 1.0); 26 | } -------------------------------------------------------------------------------- /src/shaders/5.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D colorBuffer; 4 | uniform sampler2D displacement; 5 | uniform vec2 offset; 6 | varying vec2 vUv; 7 | 8 | void main() { 9 | vec2 dUv = texture2D(displacement, vUv).xy; 10 | float strength = 0.15; 11 | dUv = vUv + dUv * strength; 12 | vec2 delta = vUv - dUv; 13 | 14 | float scale = 1.5; 15 | vec2 uv = (dUv - 0.5) * (1.0 / scale) + 0.5; 16 | uv -= offset; 17 | 18 | gl_FragColor = texture2D(colorBuffer, uv); 19 | } -------------------------------------------------------------------------------- /src/shaders/5.vert: -------------------------------------------------------------------------------- 1 | attribute vec4 position; 2 | varying vec2 vUv; 3 | 4 | void main() { 5 | vUv = (position.xy + 1.0) * 0.5; 6 | vUv.y = 1.0 - vUv.y; 7 | gl_Position = position; 8 | } -------------------------------------------------------------------------------- /src/shaders/cube-to-uv.glsl: -------------------------------------------------------------------------------- 1 | vec2 cubeToUV( vec3 v, float texelSize ) { 2 | // Horizontal cross layout: 3 | // 4 | // Y Char: Axis 5 | // xzXZ Case: Sign 6 | // y 7 | 8 | vec3 absV = abs( v ); 9 | 10 | // Intersect unit cube 11 | 12 | float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); 13 | v *= scaleToCube; 14 | absV *= scaleToCube; 15 | 16 | // Apply scale to cut the seams 17 | 18 | // two texels less per square (one texel will do for NEAREST) 19 | vec2 zoom = 1.0 - 2.0 * vec2( 4.0, 3.0 ) * texelSize; 20 | v.xy *= zoom; 21 | 22 | // Unwrap 23 | 24 | // space: -1 ... 1 range for each square 25 | // 26 | // # dim := ( 4 , 3 ) 27 | // #X## 28 | // # center := ( 1 , 1 ) 29 | 30 | vec2 planar = v.xy; 31 | 32 | float almostATexel = 1.5 * texelSize; 33 | float almostOne = 1.0 - almostATexel; 34 | 35 | if ( absV.z >= almostOne ) { 36 | 37 | if ( v.z > 0.0 ) 38 | planar.x = 4.0 - v.x; 39 | 40 | } else if ( absV.x >= almostOne ) { 41 | 42 | float signX = sign( v.x ); 43 | planar.x = v.z * signX * zoom.x + 2.0 * signX; 44 | 45 | } else if ( absV.y >= almostOne ) { 46 | 47 | float signY = sign( v.y ); 48 | planar.y = v.z * signY * zoom.y + 2.0 * signY; 49 | 50 | } 51 | 52 | // Transform to UV space 53 | 54 | // scale := 0.5 / dim 55 | // translate := ( center + 0.5 ) / dim 56 | return vec2( 0.125, 0.16666666 ) * planar + vec2( 0.375, 0.5 ); 57 | } 58 | 59 | #pragma glslify: export(cubeToUV) -------------------------------------------------------------------------------- /src/shaders/envmap-equirect.glsl: -------------------------------------------------------------------------------- 1 | // by Marcin Ignac 2 | // https://github.com/vorg/pragmatic-pbr/blob/master/local_modules/glsl-envmap-equirect/index.glsl 3 | 4 | #define PI 3.1415926 5 | #define TwoPI (2.0 * PI) 6 | 7 | /** 8 | * Samples equirectangular (lat/long) panorama environment map 9 | * @param {sampler2D} envMap - equirectangular (lat/long) panorama texture 10 | * @param {vec3} wcNormal - normal in the world coordinate space 11 | * @param {float} - flipEnvMap -1.0 for left handed coorinate system oriented texture (usual case) 12 | * 1.0 for right handed coorinate system oriented texture 13 | * @return {vec2} equirectangular texture coordinate- 14 | * @description Based on http://http.developer.nvidia.com/GPUGems/gpugems_ch17.html and http://gl.ict.usc.edu/Data/HighResProbes/ 15 | */ 16 | vec2 envMapEquirect(vec3 wcNormal, float flipEnvMap) { 17 | //I assume envMap texture has been flipped the WebGL way (pixel 0,0 is a the bottom) 18 | //therefore we flip wcNorma.y as acos(1) = 0 19 | float phi = acos(-wcNormal.y); 20 | float theta = atan(flipEnvMap * wcNormal.x, wcNormal.z) + PI; 21 | return vec2(theta / TwoPI, phi / PI); 22 | } 23 | 24 | vec2 envMapEquirect(vec3 wcNormal) { 25 | //-1.0 for left handed coordinate system oriented texture (usual case) 26 | return envMapEquirect(wcNormal, -1.0); 27 | } 28 | 29 | #pragma glslify: export(envMapEquirect) -------------------------------------------------------------------------------- /src/shaders/prefilter-cube.glsl: -------------------------------------------------------------------------------- 1 | const vec3 x = vec3(1.0, 0.0, 0.0); 2 | const vec3 y = vec3(0.0, 1.0, 0.0); 3 | const vec3 z = vec3(0.0, 0.0, 1.0); 4 | 5 | const mat3 front = mat3(x, y, z); 6 | const mat3 back = mat3(x, y, -z); 7 | const mat3 right = mat3(z, y, x); 8 | const mat3 left = mat3(z, y, -x); 9 | const mat3 top = mat3(x, z, y); 10 | const mat3 bottom = mat3(x, z, -y); 11 | const float specularity = 4.0; 12 | 13 | const float size = 16.0; 14 | const float start = ((0.5/size)-0.5)*2.0; 15 | const float end = -start; 16 | const float incr = 4.0 / size; 17 | 18 | vec4 sample (mat3 side, vec3 eyedir, vec3 base_ray){ 19 | vec3 ray = side*base_ray; 20 | float lambert = max(0.0, dot(ray, eyedir)); 21 | float term = pow(lambert, specularity)*base_ray.z; 22 | return vec4(texture(ray).rgb*term, term); 23 | } 24 | 25 | vec3 prefilter (vec3 eyedir, mat4 invProjMatrix) { 26 | vec4 result = vec4(0.0); 27 | for(float xi=start; xi<=end; xi+=incr){ 28 | for(float yi=start; yi<=end; yi+=incr){ 29 | vec3 ray = normalize( 30 | (invProjMatrix * vec4(xi, yi, 0.0, 1.0)).xyz 31 | ); 32 | result += sample(front, eyedir, ray); 33 | result += sample(back, eyedir, ray); 34 | result += sample(top, eyedir, ray); 35 | result += sample(bottom, eyedir, ray); 36 | result += sample(left, eyedir, ray); 37 | result += sample(right, eyedir, ray); 38 | } 39 | } 40 | result /= result.w; 41 | return result.rgb; 42 | } 43 | 44 | #pragma glslify: export(prefilter) -------------------------------------------------------------------------------- /src/shaders/rgbm-decode.glsl: -------------------------------------------------------------------------------- 1 | vec3 rgbmDecode (in vec4 rgbm) { 2 | return 6.0 * rgbm.rgb * rgbm.a; 3 | } 4 | 5 | #pragma glslify: export(rgbmDecode) -------------------------------------------------------------------------------- /src/shaders/rgbm-encode.glsl: -------------------------------------------------------------------------------- 1 | vec4 rgbmEncode (in vec3 color) { 2 | float minB = 0.000001; 3 | vec4 rgmb = vec4(0.0); 4 | color *= 1.0 / 6.0; 5 | rgbm.a = clamp(max(max(color.r, color.g), max(color.b, minB)), 0.0, 1.0); 6 | rgbm.a = ceil(rgbm.a * 255.0) / 255.0; 7 | rgbm.rgb = color / rgbm.a; 8 | return rgbm; 9 | } 10 | 11 | #pragma glslify: export(rgbmEncode) -------------------------------------------------------------------------------- /src/shaders/rotation-zyx.glsl: -------------------------------------------------------------------------------- 1 | mat3 rot_x( float a ) { 2 | 3 | float s = sin( a ), c = cos( a ); 4 | 5 | return mat3( 6 | 1.0 , 0.0 , 0.0 , 7 | 0.0 , +c , +s , 8 | 0.0 , -s , +c ); 9 | } 10 | 11 | mat3 rot_y( float a ) { 12 | 13 | float s = sin( a ), c = cos( a ); 14 | 15 | return mat3( 16 | +c , 0.0 , -s , 17 | 0.0 , 1.0 , 0.0 , 18 | +s , 0.0 , +c ); 19 | } 20 | 21 | mat3 rot_z( float a ) { 22 | 23 | float s = sin( a ), c = cos( a ); 24 | 25 | return mat3( 26 | +c , +s , 0.0 , 27 | -s , +c , 0.0 , 28 | 0.0 , 0.0 , 1.0 ); 29 | } 30 | 31 | mat3 rot_zyx( vec3 xyz ) { 32 | 33 | return rot_z( xyz.z ) * rot_x( xyz.x ) * rot_y( xyz.y ); 34 | 35 | } 36 | 37 | #pragma glslify: export(rot_zyx) -------------------------------------------------------------------------------- /src/shaders/sdf.js: -------------------------------------------------------------------------------- 1 | var assign = require('object-assign') 2 | 3 | module.exports = function(opt) { 4 | opt = opt||{} 5 | var opacity = typeof opt.opacity === 'number' ? opt.opacity : 1 6 | var alphaTest = typeof opt.alphaTest === 'number' ? opt.alphaTest : 0.06 7 | return assign({ 8 | uniforms: { 9 | opacity: { type: 'f', value: opacity }, 10 | map: { type: 't', value: opt.map || new THREE.Texture() }, 11 | color: { type: 'c', value: new THREE.Color(opt.color) } 12 | }, 13 | vertexShader: [ 14 | "attribute vec2 uv;", 15 | "attribute vec3 position;", 16 | "uniform mat4 projectionMatrix;", 17 | "uniform mat4 modelViewMatrix;", 18 | "varying vec2 vUv;", 19 | "void main() {", 20 | "vUv = uv;", 21 | "gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xyz, 1.0);", 22 | "}" 23 | ].join("\n"), 24 | fragmentShader: [ 25 | "#extension GL_OES_standard_derivatives : enable", 26 | "precision mediump float;", 27 | "#define SQRT2_2 0.70710678118654757", 28 | "uniform float opacity;", 29 | "uniform vec3 color;", 30 | "uniform sampler2D map;", 31 | "varying vec2 vUv;", 32 | "void main() {", 33 | "vec4 texColor = texture2D(map, vUv);", 34 | "float dst = texColor.a;", 35 | "float afwidth = length(vec2(dFdx(dst), dFdy(dst))) * SQRT2_2;", 36 | "float alpha = smoothstep(0.5 - afwidth, 0.5 + afwidth, dst);", 37 | "vec4 base = vec4(color, opacity * alpha);", 38 | "gl_FragColor = base;", 39 | "}" 40 | ].join("\n"), 41 | defines: { 42 | "USE_MAP": "", 43 | "ALPHATEST": Number(alphaTest || 0).toFixed(1) 44 | } 45 | }, opt) 46 | } 47 | -------------------------------------------------------------------------------- /src/shaders/sky.js: -------------------------------------------------------------------------------- 1 | // http://threejs.org/examples/#webgl_shaders_sky 2 | 3 | /** 4 | * @author zz85 / https://github.com/zz85 5 | * 6 | * Based on "A Practical Analytic Model for Daylight" 7 | * aka The Preetham Model, the de facto standard analytic skydome model 8 | * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf 9 | * 10 | * First implemented by Simon Wallner 11 | * http://www.simonwallner.at/projects/atmospheric-scattering 12 | * 13 | * Improved by Martin Upitis 14 | * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR 15 | * 16 | * Three.js integration by zz85 http://twitter.com/blurspline 17 | */ 18 | 19 | var glslify = require('glslify') 20 | 21 | THREE.ShaderLib[ 'sky' ] = { 22 | 23 | uniforms: { 24 | 25 | luminance: { type: "f", value: 1 }, 26 | turbidity: { type: "f", value: 2 }, 27 | reileigh: { type: "f", value: 1 }, 28 | mieCoefficient: { type: "f", value: 0.005 }, 29 | mieDirectionalG: { type: "f", value: 0.8 }, 30 | sunPosition: { type: "v3", value: new THREE.Vector3() } 31 | 32 | }, 33 | 34 | vertexShader: [ 35 | 36 | "varying vec3 vWorldPosition;", 37 | 38 | "void main() {", 39 | 40 | "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", 41 | "vWorldPosition = worldPosition.xyz;", 42 | 43 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 44 | 45 | "}", 46 | 47 | ].join( "\n" ), 48 | 49 | fragmentShader: glslify(__dirname + '/sky.frag') 50 | 51 | }; 52 | 53 | module.exports = function Sky () { 54 | 55 | var skyShader = THREE.ShaderLib[ "sky" ]; 56 | var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms ); 57 | 58 | var skyMat = new THREE.ShaderMaterial( { 59 | fragmentShader: skyShader.fragmentShader, 60 | vertexShader: skyShader.vertexShader, 61 | uniforms: skyUniforms, 62 | side: THREE.DoubleSide, 63 | } ); 64 | 65 | var skyGeo = new THREE.SphereGeometry( 450000, 32, 15 ); 66 | var skyMesh = new THREE.Mesh( skyGeo, skyMat ); 67 | 68 | // Expose variables 69 | this.mesh = skyMesh; 70 | this.uniforms = skyUniforms; 71 | }; -------------------------------------------------------------------------------- /src/shaders/src-over.glsl: -------------------------------------------------------------------------------- 1 | vec4 blend(vec4 background, vec4 foreground) { 2 | return (foreground.rgba * foreground.a) + background.rgba * (1.0 - foreground.a); 3 | } 4 | 5 | #pragma glslify: export(blend) -------------------------------------------------------------------------------- /src/shaders/texture-query-lod.glsl: -------------------------------------------------------------------------------- 1 | float mipmapLevel (in vec2 uv) { 2 | vec2 dxVtc = dFdx(uv); 3 | vec2 dyVtc = dFdy(uv); 4 | float deltaMaxSqr = max(dot(dxVtc, dxVtc), dot(dyVtc, dyVtc)); 5 | float mml = 0.5 * log2(deltaMaxSqr); 6 | return max(0.0, mml); 7 | } 8 | 9 | #pragma glslify: export(mipmapLevel) -------------------------------------------------------------------------------- /src/shaders/tonemap-filmic.glsl: -------------------------------------------------------------------------------- 1 | //Based on Filmic Tonemapping Operators http://filmicgames.com/archives/75 2 | vec3 tonemapFilmic(vec3 color) { 3 | vec3 x = max(vec3(0.0), color - 0.004); 4 | return (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); 5 | } 6 | 7 | #pragma glslify: export(tonemapFilmic) -------------------------------------------------------------------------------- /src/shaders/tonemap-reinhard.glsl: -------------------------------------------------------------------------------- 1 | //Based on Filmic Tonemapping Operators http://filmicgames.com/archives/75 2 | vec3 tonemapReinhard(vec3 color) { 3 | return color / (color + vec3(1.0)); 4 | } 5 | 6 | #pragma glslify: export(tonemapReinhard) -------------------------------------------------------------------------------- /src/shaders/tonemap-uncharted2.glsl: -------------------------------------------------------------------------------- 1 | float A = 0.15; 2 | float B = 0.50; 3 | float C = 0.10; 4 | float D = 0.20; 5 | float E = 0.02; 6 | float F = 0.30; 7 | float W = 11.2; 8 | 9 | vec3 Uncharted2Tonemap(vec3 x) { 10 | return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; 11 | } 12 | 13 | //Based on Filmic Tonemapping Operators http://filmicgames.com/archives/75 14 | vec3 tonemapUncharted2(vec3 color) { 15 | float ExposureBias = 2.0; 16 | vec3 curr = Uncharted2Tonemap(ExposureBias * color.rgb); 17 | 18 | vec3 whiteScale = 1.0 / Uncharted2Tonemap(vec3(W)); 19 | return curr * whiteScale; 20 | } 21 | 22 | #pragma glslify: export(tonemapUncharted2) -------------------------------------------------------------------------------- /src/simplicial-with-uv.js: -------------------------------------------------------------------------------- 1 | let complex = sphere(1, { segments: 32 }) 2 | let geometry = Simplicial(complex) 3 | complex.cells.forEach((cell, i) => { 4 | const [ a, b, c ] = cell 5 | const uvs = complex.uvs 6 | geometry.faceVertexUvs[0].push([ 7 | new THREE.Vector2().fromArray(uvs[a]), 8 | new THREE.Vector2().fromArray(uvs[b]), 9 | new THREE.Vector2().fromArray(uvs[c]) 10 | ]) 11 | 12 | const normals = complex.normals 13 | geometry.faces[i].vertexNormals = [ 14 | new THREE.Vector3().fromArray(normals[a]), 15 | new THREE.Vector3().fromArray(normals[b]), 16 | new THREE.Vector3().fromArray(normals[c]) 17 | ] 18 | }) -------------------------------------------------------------------------------- /src/three-orbit-app.js: -------------------------------------------------------------------------------- 1 | var createLoop = require('canvas-loop') 2 | var createControls = require('orbit-controls') 3 | var assign = require('object-assign') 4 | var DeviceOrientationControls = require('./gl/DeviceOrientationControls') 5 | 6 | module.exports = createApp 7 | function createApp (opt) { 8 | opt = assign({ 9 | distance: 2, 10 | scale: Math.min(window.devicePixelRatio, 2) 11 | }, opt) 12 | 13 | var distance = typeof opt.distance === 'number' ? opt.distance : 5 14 | var canvas = opt.canvas 15 | if (!canvas) { 16 | canvas = document.createElement('canvas') 17 | document.body.appendChild(canvas) 18 | } 19 | 20 | var renderer = new THREE.WebGLRenderer(assign({}, opt, { 21 | canvas: canvas, 22 | devicePixelRatio: opt.scale 23 | })) 24 | 25 | renderer.setClearColor(0x000000, 1) 26 | 27 | const iPhone = /(iPhone|iPad)/i.test(navigator.userAgent) 28 | const tmp2 = [0, 0] 29 | 30 | // annoying bug which sometimes shows with content scrolled down on iPhone 31 | if (iPhone && window.innerWidth > window.innerHeight) { 32 | setTimeout(() => window.scrollTo(undefined, 0), 2000) 33 | } 34 | 35 | var app = createLoop(canvas, assign({ 36 | parent: () => { 37 | // brutal bug with iPhone where the top/bottom status bars 38 | // will appear unless the canvas is at least + 1 px higher than window 39 | var off = (window.innerWidth > window.innerHeight && iPhone) ? 1 : 0 40 | tmp2[0] = window.innerWidth + off 41 | tmp2[1] = window.innerHeight + off 42 | return tmp2 43 | } 44 | }, opt)) 45 | 46 | var target = new THREE.Vector3() 47 | var scene = new THREE.Scene() 48 | var fov = typeof opt.fov === 'number' ? opt.fov : 50 49 | var aspect = app.shape[0] / app.shape[1] 50 | var near = typeof opt.near === 'number' ? opt.near : 0.001 51 | var far = typeof opt.far === 'number' ? opt.far : 1000 52 | var camera = new THREE.PerspectiveCamera(fov, aspect, near, far) 53 | camera.position.fromArray(opt.position || [ 0, 0, -distance ]) 54 | 55 | var deviceOrientationControls = opt.deviceOrientationControls 56 | var controls 57 | if (deviceOrientationControls) { 58 | controls = new DeviceOrientationControls(camera) 59 | } else { 60 | controls = createControls(assign({}, opt, { 61 | canvas: canvas, 62 | parent: window 63 | })) 64 | } 65 | 66 | app.on('tick', render) 67 | app.on('resize', resize) 68 | resize() 69 | 70 | canvas.addEventListener('touchstart', (ev) => ev.preventDefault()) 71 | 72 | app.canvas = canvas 73 | app.scene = scene 74 | app.camera = camera 75 | app.controls = controls 76 | app.renderer = renderer 77 | app.render = render 78 | app.resize = resize 79 | app.target = target 80 | app.composer = null 81 | 82 | return app.start() 83 | 84 | function render () { 85 | updateControls() 86 | if (app.composer) app.composer.render() 87 | else renderer.render(scene, camera) 88 | } 89 | 90 | function updateControls () { 91 | if (deviceOrientationControls) { 92 | controls.update() 93 | } else { 94 | var position = camera.position.toArray() 95 | var direction = target.toArray() 96 | controls.update(position, direction) 97 | camera.position.fromArray(position) 98 | camera.lookAt(target.fromArray(direction)) 99 | } 100 | } 101 | 102 | function resize () { 103 | var width = app.shape[0] 104 | var height = app.shape[1] 105 | camera.aspect = width / height 106 | renderer.setSize(width, height) 107 | camera.updateProjectionMatrix() 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/util/acorn-types.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'ArrayExpression', 3 | 'AssignmentExpression', 4 | 'BinaryExpression', 5 | 'BlockStatement', 6 | 'BreakStatement', 7 | 'CallExpression', 8 | 'CatchClause', 9 | 'ClassDeclaration', 10 | 'ComprehensionExpression', 11 | 'ConditionalExpression', 12 | 'ContinueStatement', 13 | 'DebuggerStatement', 14 | 'DoWhileStatement', 15 | 'EmptyStatement', 16 | 'ExportDeclaration', 17 | 'ExportNamedDeclaration', 18 | 'Expression', 19 | 'ExpressionStatement', 20 | 'ForInStatement', 21 | 'ForInit', 22 | 'ForStatement', 23 | 'ArrowFunctionExpression', 24 | 'Function', 25 | 'FunctionDeclaration', 26 | 'FunctionExpression', 27 | 'Identifier', 28 | 'IfStatement', 29 | 'ImportDeclaration', 30 | 'ImportSpecifier', 31 | 'ImportDefaultSpecifier', 32 | 'LabeledStatement', 33 | 'Literal', 34 | 'LogicalExpression', 35 | 'MemberExpression', 36 | 'MethodDefinition', 37 | 'NewExpression', 38 | 'ObjectExpression', 39 | 'ObjectPattern', 40 | 'Program', 41 | 'ReturnStatement', 42 | 'ScopeBody', 43 | 'SequenceExpression', 44 | 'Statement', 45 | 'SwitchCase', 46 | 'SwitchStatement', 47 | 'TaggedTemplateExpression', 48 | 'ThisExpression', 49 | 'ThrowStatement', 50 | 'TryStatement', 51 | 'UnaryExpression', 52 | 'UpdateExpression', 53 | 'VariableDeclaration', 54 | 'VariableDeclarator', 55 | 'WhileStatement', 56 | 'WithStatement', 57 | 'Property' 58 | ] 59 | -------------------------------------------------------------------------------- /src/util/audio-demo-2d.js: -------------------------------------------------------------------------------- 1 | import badge from 'soundcloud-badge' 2 | import desktopOnly from '../desktop-only' 3 | import isMobile from '../is-mobile' 4 | import createErrorPage from '../fatal-error' 5 | import createAnalyser from 'web-audio-analyser' 6 | import canvasLoop from 'canvas-loop' 7 | import css from 'dom-css' 8 | import assign from 'object-assign' 9 | 10 | const noop = () => {} 11 | const error = createErrorPage() 12 | const AudioContext = window.AudioContext || window.webkitAudioContext 13 | const id = 'b95f61a90da961736c03f659c03cb0cc' 14 | 15 | export default function audioDemo (track, opt, render) { 16 | if (typeof opt === 'function') { 17 | render = opt 18 | opt = {} 19 | } 20 | opt = opt || {} 21 | render = render || noop 22 | 23 | let canvas = opt.canvas 24 | if (!canvas) { 25 | document.body.style.margin = '0' 26 | document.body.style.overflow = 'hidden' 27 | canvas = document.body.appendChild(document.createElement('canvas')) 28 | } 29 | 30 | const context = canvas.getContext('2d') 31 | const app = canvasLoop(canvas, assign({ 32 | scale: window.devicePixelRatio 33 | }, opt)) 34 | 35 | if (isMobile() || !AudioContext) { 36 | desktopOnly() 37 | } else { 38 | badge({ 39 | client_id: id, 40 | song: track, 41 | dark: opt.dark !== false, 42 | getFonts: true 43 | }, function (err, src, data, div) { 44 | if (err) return error(err) 45 | play(src, data, div) 46 | }) 47 | } 48 | 49 | return app 50 | 51 | function play (src, data, div) { 52 | const audio = new window.Audio() 53 | audio.crossOrigin = 'Anonymous' 54 | audio.src = src 55 | audio.play() 56 | 57 | const analyser = createAnalyser(audio, { stereo: false }) 58 | app.emit('ready', { analyser, audio, data, div }) 59 | 60 | if (opt.center) { 61 | app.once('tick', resize) 62 | app.on('resize', resize) 63 | } 64 | 65 | app.on('tick', tick) 66 | app.start() 67 | 68 | function tick (dt) { 69 | const [width, height] = app.shape 70 | 71 | context.save() 72 | context.scale(app.scale, app.scale) 73 | context.clearRect(0, 0, width, height) 74 | app.emit('render', context, analyser, dt) 75 | context.restore() 76 | } 77 | 78 | function resize () { 79 | const [width, height] = app.shape 80 | css(canvas, { 81 | left: (window.innerWidth - width) / 2, 82 | top: (window.innerHeight - height) / 2 83 | }) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/util/punch-card.js: -------------------------------------------------------------------------------- 1 | // https://github.com/jfhbrook/node-punchcard/blob/master/punch.js 2 | var ebcdic = require('punchcard/ebcdic') 3 | 4 | module.exports = function txt2punch (txt) { 5 | var buff = txt 6 | if (typeof txt === 'string') { 7 | buff = Buffer(txt) 8 | } 9 | 10 | buff = ebcdic.toEBCDIC(buff) 11 | 12 | // src: http://www.divms.uiowa.edu/~jones/cards/codes.html 13 | return Array.prototype.map.call(buff, function (char) { 14 | var row = []; // Not a bitmask. ;) 15 | 16 | // Some characters are "special cases." 17 | switch (char) { 18 | case 0x00: 19 | row = [12, 0, 1, 8, 9] 20 | break 21 | case 0x20: 22 | row = [11, 0, 1, 8, 9] 23 | break 24 | case 0x40: 25 | row = [] 26 | break 27 | case 0x50: 28 | row = [12] 29 | break 30 | case 0x60: 31 | row = [11] 32 | break 33 | default: 34 | // Defined in next section (gets hoisted) 35 | row = calcRow() 36 | break 37 | } 38 | 39 | // Most of these can be calculated. 40 | function calcRow () { 41 | // Bitmasks! 42 | var left = (char & 0xF0) >> 4, 43 | right = char & 0x0F, 44 | row = [] 45 | 46 | // Handle the LHS 47 | if (right > 9) { 48 | row.push(8) 49 | row.push(right - 8) 50 | } else { 51 | row.push(right) 52 | } 53 | 54 | // Abusing fall-through \m/ 55 | switch (left) { 56 | case 0x00: 57 | case 0x04: 58 | case 0x08: 59 | case 0x0C: 60 | row.push(12) 61 | break 62 | } 63 | 64 | switch (left) { 65 | case 0x01: 66 | case 0x05: 67 | case 0x09: 68 | case 0x0A: 69 | case 0x0D: 70 | row.push(11) 71 | break 72 | } 73 | 74 | switch (left) { 75 | case 0x02: 76 | case 0x06: 77 | case 0x08: 78 | case 0x0A: 79 | case 0x0E: 80 | row.push(10) 81 | break 82 | } 83 | 84 | switch (left) { 85 | case 0x00: 86 | case 0x01: 87 | case 0x02: 88 | row.push(9) 89 | break 90 | } 91 | return row 92 | } 93 | 94 | const a = row.slice(0, 9) 95 | a.reverse() 96 | 97 | const b = row.slice(9) 98 | return a.concat(b) 99 | 100 | // |987654321ABC| 101 | // var line = ' ' 102 | 103 | // row.forEach(function (i) { 104 | // line = line.substring(0, i - 1) + '▘' + line.substring(i, line.length) 105 | // }) 106 | 107 | // line = line.substring(0, 9).split('').reverse().join('') 108 | // + line.substring(9, line.length) 109 | 110 | // return '|' + line.split('').join('') + ' |' 111 | }) 112 | } 113 | -------------------------------------------------------------------------------- /static/loader.css: -------------------------------------------------------------------------------- 1 | /* Modified from: http://projects.lukehaas.me/css-loaders/ */ 2 | .loader { 3 | margin: 0px auto; 4 | font-size: 10px; 5 | font: sans-serif; 6 | position: relative; 7 | text-indent: -9999em; 8 | border-top: 0.2em solid rgba(0, 0, 0, 0.2); 9 | border-right: 0.2em solid rgba(0, 0, 0, 0.2); 10 | border-bottom: 0.2em solid rgba(0, 0, 0, 0.2); 11 | border-left: 0.2em solid #000; 12 | -webkit-transform: translateZ(0); 13 | -ms-transform: translateZ(0); 14 | transform: translateZ(0); 15 | -webkit-animation: spin 1.1s infinite linear; 16 | animation: spin 1.1s infinite linear; 17 | } 18 | .loader, 19 | .loader:after { 20 | border-radius: 50%; 21 | width: 3em; 22 | height: 3em; 23 | position: absolute; 24 | margin: auto; 25 | top: 0; 26 | left: 0; 27 | right: 0; 28 | bottom: 0; 29 | } 30 | @-webkit-keyframes spin { 31 | 0% { 32 | -webkit-transform: rotate(0deg); 33 | transform: rotate(0deg); 34 | } 35 | 100% { 36 | -webkit-transform: rotate(360deg); 37 | transform: rotate(360deg); 38 | } 39 | } 40 | @keyframes spin { 41 | 0% { 42 | -webkit-transform: rotate(0deg); 43 | transform: rotate(0deg); 44 | } 45 | 100% { 46 | -webkit-transform: rotate(360deg); 47 | transform: rotate(360deg); 48 | } 49 | } 50 | --------------------------------------------------------------------------------