Initial State Commit

This commit is contained in:
Daniel Legt 2023-05-01 15:47:25 +03:00
commit eea5b6cdf4
26 changed files with 1002 additions and 0 deletions

31
go.mod Normal file
View File

@ -0,0 +1,31 @@
module danlegt.com/stablediffusion-friends
go 1.20
require github.com/gin-gonic/gin v1.9.0
require (
github.com/bytedance/sonic v1.8.8 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.13.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

82
go.sum Normal file
View File

@ -0,0 +1,82 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

82
lib/rtypes/sdnode.go Normal file
View File

@ -0,0 +1,82 @@
package rtypes
import "fmt"
// A stable diffusion node
type SDNode struct {
URL string `json:"url"`
Port int `json:"port"`
Status bool `json:"status"`
}
func (node SDNode) GetDomainBase() string {
return fmt.Sprintf("http://%v:%v/", node.URL, node.Port)
}
func (node SDNode) GetTextToImageLink() string {
return fmt.Sprintf("%vsdapi/v1/txt2img", node.GetDomainBase())
}
type SDTextToImageRequest struct {
Prompt string `json:"prompt"` // "a beautiful blonde woman, woman, blonde, beautiful, masterpiece, illustration",
SamplerName string `json:"sampler_name"` // "Euler",
BatchSize int `json:"batch_size"` // 1,
Steps int `json:"steps"` // 25,
CfgScale float32 `json:"cfg_scale"` // 7,
Width int `json:"width"` // 512,
Height int `json:"height"` // 512,
NegativePrompt string `json:"negative_prompt"` // "ugly, destroyed, artifacts",
SamplerIndex string `json:"sampler_index"` // "Euler",
SendImages bool `json:"send_images"` // true,
SaveImages bool `json:"save_images"` // false
}
type SDTextToImageResponse struct {
Images []string `json:"images"`
Parameters SDTextToImageResponseParameters `json:"parameters"`
Info string `json:"info"`
}
type SDTextToImageResponseParameters struct {
EnableHDR bool `json:"enable_hr"` // false,
DenoisingStrength float32 `json:"denoising_strength"` // 0,
FirstphaseWidth float32 `json:"firstphase_width"` // 0,
FirstphaseHeight float32 `json:"firstphase_height"` // 0,
HrScale float32 `json:"hr_scale"` // 2,
HrUpscaler string `json:"hr_upscaler"` // null,
HrSecondPassSteps float32 `json:"hr_second_pass_steps"` // 0,
HrResizeX float32 `json:"hr_resize_x"` // 0,
HrResizeY float32 `json:"hr_resize_y"` // 0,
Prompt string `json:"prompt"` // "a beautiful blonde woman, woman, blonde, beautiful, masterpiece, illustration",
Styles []string `json:"styles"` // null,
Seed float32 `json:"seed"` // -1,
Subseed float32 `json:"subseed"` // -1,
SubseedStrength float32 `json:"subseed_strength"` // 0,
SeedResizeFromH float32 `json:"seed_resize_from_h"` // -1,
SeedResizeFromW float32 `json:"seed_resize_from_w"` // -1,
Sampler_name string `json:"sampler_name"` // "Euler",
Batch_size float32 `json:"batch_size"` // 1,
NIterations float32 `json:"n_iter"` // 1,
Steps float32 `json:"steps"` // 25,
CFGScale float32 `json:"cfg_scale"` // 7,
Width int `json:"width"` // 512,
Height int `json:"height"` // 512,
RestoreFaces bool `json:"restore_faces"` // false,
Tiling bool `json:"tiling"` // false,
DoNotSaveSamples bool `json:"do_not_save_samples"` // false,
DoNotSaveGrid bool `json:"do_not_save_grid"` // false,
NegativePrompt string `json:"negative_prompt"` // "ugly, destroyed, artifacts",
Eta string `json:"eta"` // null,
Schurn float32 `json:"s_churn"` // 0,
Stmax string `json:"s_tmax"` // null,
Stmin float32 `json:"s_tmin"` // 0,
Snoise float32 `json:"s_noise"` // 1,
OverrideSettings string `json:"override_settings"` // null,
OverrideSettingsRestoreAfterwards bool `json:"override_settings_restore_afterwards"` // true,
ScriptArgs []string `json:"script_args"` // [],
SamplerIndex string `json:"sampler_index"` // "Euler",
ScriptName string `json:"script_name"` // null,
SendImages bool `json:"send_images"` // true,
SaveImages bool `json:"save_images"` // false,
AlwaysonScripts interface{} `json:"alwayson_scripts"` // {}
}

125
main.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"danlegt.com/stablediffusion-friends/lib/rtypes"
"github.com/gin-gonic/gin"
)
var nodes [1]rtypes.SDNode = [...]rtypes.SDNode{
rtypes.SDNode{
URL: "127.0.0.1",
Port: 7860,
Status: false,
},
}
var imagesToDisplay []string
func main() {
r := gin.Default()
runLoaders(r)
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.POST("/api/image/generate", func(c *gin.Context) {
imageDescriptior := c.PostForm("image_description")
// Build the request
request := rtypes.SDTextToImageRequest{
Prompt: imageDescriptior,
SamplerName: "DPM2",
BatchSize: 1,
Steps: 25,
CfgScale: 8,
Width: 512,
Height: 512,
NegativePrompt: "nsfw, porn, naked, nude, nipple, penis, dick, vagina, asshole, visible nipple, nsfl, not safe for work, nudity, artifact, deformed, multiple limbs, ugly, gore, blood, sex, pornography",
SamplerIndex: "Euler",
SendImages: true,
SaveImages: false,
}
body, err := json.Marshal(request)
if err != nil {
c.AbortWithError(500, err)
return
}
r, err := http.NewRequest("POST", nodes[0].GetTextToImageLink(), bytes.NewBuffer(body))
if err != nil {
c.AbortWithError(500, err)
return
}
r.Header.Add("Content-Type", "application/json")
client := &http.Client{}
res, err := client.Do(r)
if err != nil {
c.AbortWithError(500, err)
return
}
defer res.Body.Close()
responseJson := &rtypes.SDTextToImageResponse{}
err = json.NewDecoder(res.Body).Decode(responseJson)
if err != nil {
fmt.Println("Failed to decode the JSON")
c.AbortWithError(500, err)
return
}
c.JSON(http.StatusOK, responseJson)
})
r.POST("/api/image/submit", func(c *gin.Context) {
imageData := c.PostForm("image_data")
imagesToDisplay = append(imagesToDisplay[:], imageData)
fmt.Printf("Got a new image to display, currently %v in queue\n", len(imagesToDisplay))
c.JSON(200, gin.H{
"queue_position": len(imagesToDisplay),
})
})
r.GET("/api/image", func(c *gin.Context) {
if len(imagesToDisplay) <= 0 {
c.JSON(200, [0]string{})
return
}
newImage, newImagesToDisplay := imagesToDisplay[0], imagesToDisplay[1:]
// Actually pop it
imagesToDisplay = newImagesToDisplay
c.JSON(200, gin.H{
"image": newImage,
"queue_left": len(imagesToDisplay),
})
})
r.GET("/api/image/queue", func(c *gin.Context) {
c.JSON(200, imagesToDisplay)
})
r.Run()
}
func runLoaders(r *gin.Engine) {
r.Static("/public", "./public")
r.LoadHTMLGlob("templates/**/*.html")
}

46
public/css/buttons.css Normal file
View File

@ -0,0 +1,46 @@
.btn-95 {
background-color: #c0c0c0;
color: black;
border: 2px outset gray;
border-radius: 0px;
font-size: 16px;
font-family: "Arial", sans-serif;
padding: 6px 12px;
text-align: center;
text-decoration: none;
display: inline-block;
cursor: pointer;
}
.btn-95:hover:not(:disabled) {
background-color: #d8d8d8;
}
.btn-95:active:not(:disabled) {
background-color: #b8b8b8;
border: 2px inset gray;
}
.btn-95:disabled {
cursor: not-allowed;
opacity: .8;
}
textarea {
background-color: #c0c0c0;
color: black;
border: 2px outset gray;
font-size: 16px;
font-family: "Arial", sans-serif;
padding: 6px 12px;
resize: vertical;
}
textarea:hover {
background-color: #d8d8d8;
}
textarea:focus {
background-color: #e8e8e8;
}

67
public/css/vaporwave.css Normal file
View File

@ -0,0 +1,67 @@
.overlay {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: transparent;
background-size: 5px 5px, 5px 5px;
background-position: -1px -1px, -1px -1px;
background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
mix-blend-mode: difference;
}
.glitch {
clip-path: polygon(0 0, 100% 0, 100% 0.5em, 0 0.5em);
animation: glitch 10s linear infinite;
transform: translatex(0.1rem);
}
@keyframes glitch {
to {
clip-path: polygon(0 calc(100% - .5em), 100% calc(100% - .5em), 0 100%, 0 100%);
}
}
.sun {
width: 40vh;
height: 40vh;
font-size: 1rem;
border-radius: 20vh;
position: relative;
overflow: hidden;
}
.sun:before {
content: "";
display: block;
position: absolute;
top: 0;
height: 50%;
background-color: #f54171;
background: linear-gradient(0deg, #2b1165 0%, #1a3a82 37%, #ab24b1 69%, #f54171 100%);
background-size: 40vh 40vh;
width: 100%;
}
.sun:after {
content: "";
display: block;
position: absolute;
bottom: 0;
width: 100%;
height: 50%;
background-color: #f54171;
background: linear-gradient(0deg, #2b1165 0%, #1a3a82 37%, #ab24b1 69%, #f54171 100%);
background-size: 40vh 40vh;
background-position: bottom center;
clip-path: polygon(0 -10em, 100% -10em, 100% -10.5em, 0 -10.5em, 0 -9em, 100% -9em, 100% -9.5em, 0 -9.5em, 0 -8em, 100% -8em, 100% -8.5em, 0 -8.5em, 0 -7em, 100% -7em, 100% -7.5em, 0 -7.5em, 0 -6em, 100% -6em, 100% -6.5em, 0 -6.5em, 0 -5em, 100% -5em, 100% -5.5em, 0 -5.5em, 0 -4em, 100% -4em, 100% -4.5em, 0 -4.5em, 0 -3em, 100% -3em, 100% -3.5em, 0 -3.5em, 0 -2em, 100% -2em, 100% -2.5em, 0 -2.5em, 0 -1em, 100% -1em, 100% -1.5em, 0 -1.5em, 0 0, 100% 0, 100% -0.5em, 0 -0.5em, 0 0, 100% 0, 100% 0.5em, 0 0.5em, 0 1em, 100% 1em, 100% 1.5em, 0 1.5em, 0 2em, 100% 2em, 100% 2.5em, 0 2.5em, 0 3em, 100% 3em, 100% 3.5em, 0 3.5em, 0 4em, 100% 4em, 100% 4.5em, 0 4.5em, 0 5em, 100% 5em, 100% 5.5em, 0 5.5em, 0 6em, 100% 6em, 100% 6.5em, 0 6.5em, 0 7em, 100% 7em, 100% 7.5em, 0 7.5em, 0 8em, 100% 8em, 100% 8.5em, 0 8.5em, 0 9em, 100% 9em, 100% 9.5em, 0 9.5em);
animation: lightEffect 20s linear infinite reverse;
}
@keyframes lightEffect {
to {
clip-path: polygon(0 0, 100% 0, 100% 0.5em, 0 0.5em, 0 1em, 100% 1em, 100% 1.5em, 0 1.5em, 0 2em, 100% 2em, 100% 2.5em, 0 2.5em, 0 3em, 100% 3em, 100% 3.5em, 0 3.5em, 0 4em, 100% 4em, 100% 4.5em, 0 4.5em, 0 5em, 100% 5em, 100% 5.5em, 0 5.5em, 0 6em, 100% 6em, 100% 6.5em, 0 6.5em, 0 7em, 100% 7em, 100% 7.5em, 0 7.5em, 0 8em, 100% 8em, 100% 8.5em, 0 8.5em, 0 9em, 100% 9em, 100% 9.5em, 0 9.5em, 0 10em, 100% 10em, 100% 10em, 0 10em, 0 11em, 100% 11em, 100% 11.5em, 0 11.5em, 0 12em, 100% 12em, 100% 12.5em, 0 12.5em, 0 13em, 100% 13em, 100% 13.5em, 0 13.5em, 0 14em, 100% 14em, 100% 14.5em, 0 14.5em, 0 15em, 100% 15em, 100% 15.5em, 0 15.5em, 0 16em, 100% 16em, 100% 16.5em, 0 16.5em, 0 17em, 100% 17em, 100% 17.5em, 0 17.5em, 0 18em, 100% 18em, 100% 18.5em, 0 18.5em, 0 19em, 100% 19em, 100% 19.5em, 0 19.5em, 0 20em, 100% 20em, 100% 20.5em, 0 20.5em);
}
}

BIN
public/img/Daniel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/img/gifs/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- old school pixellated netscape broken image icon -->
<svg viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g id="borders-and-backgrounds">
<path d="M0,0 L10,0 L10,1 L1,1 L1,15 L2,15 L2,16 L0,16 Z" fill="black"></path>
<path d="M10,4 l4,0 l0,3 l-1,0 l0,-2 l-3,0 Z" fill="black"></path>
<path d="M14,16 l0,-6 l-1,0 l0,5 l-5,0 l0,1 Z" fill="black"></path>
<path d="M12,14 l0,-3 l-1,0 l0,2 l-2,0 l0,1 Z" fill="#bcbcc3"></path>
<path d="M10,0 l1,0 l0,1 l1,0 l0,1 l1,0 l0,1 l1,0 l0,1 l-1,0 l0,-1 l-1,0 l0,-1 l-1,0 l0,2 l-1,0 Z" fill="#878787"></path>
<path d="M2,2 l8,0 l0,3 l2,0 l0,2 l-4,0 l0,5 l-2,0 l0,1 l-2,0 l0,1 l-2,0 Z" fill="#bcbcc3"></path>
</g>
<g id="lefteye">
<path d="M5,3 l2,0 l0,3 l-3,0 l0,-2 l1,0 l0,1 l1,0 l0,-1 l-1,0 Z" fill="#00891e"></path>
<path d="M5,4 l1,0 l0,1 l-1,0 Z" fill="#00f248" ></path>
<path d="M7,4 l1,0 l0,2 l-1,0 l0,1 l-2,0 l0,-1 l2,0 Z" fill="black" ></path>
</g>
<g id="righteye">
<path d="M8,7 l3,0 l0,2 l-1,0 l0,-1 l-1,0 l0,1 l1,0 l0,1 l-2,0 Z" fill="#0064fb"></path>
<path d="M9,8 l1,0 l0,1 l-1,0 Z" fill="#00fbfe"></path>
<path d="M10,9 l1,0 l0,1 l-1,0 Z" fill="#003293"></path>
<path d="M11,7 l1,0 l0,2 l-1,0 Z" fill="black"></path>
<path d="M8,10 l2,0 l0,1 l-2,0 Z" fill="black"></path>
</g>
<g id="mouth">
<path d="M3,8 l1,0 l0,1 l1,0 l0,1 l1,0 l0,1 l1,0 l0,1 l-1,0 l0,-1 l-1,0 l0,-1 l-1,0 l0,-1 l-1,0 Z" fill="#ff3900"></path>
<path d="M3,9 l1,0 l0,1 l1,0 l0,1 l1,0 l0,1 l-3,0 Z" fill="#f73ae1"></path>
<path d="M3,12 l3,0 l0,1 l-3,0 Z" fill="black"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/img/posters/1.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

BIN
public/img/posters/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

BIN
public/img/posters/3.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
public/img/posters/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

BIN
public/img/vaporwave/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
public/img/vaporwave/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

BIN
public/img/vaporwave/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
public/img/vaporwave/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

BIN
public/img/vaporwave/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
public/img/vaporwave/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
public/img/vaporwave/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

BIN
public/img/vaporwave/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

BIN
public/img/vaporwave/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

187
public/js/main.js Normal file
View File

@ -0,0 +1,187 @@
document.addEventListener(`DOMContentLoaded`, e => {
handleImageGeneratorForm();
})
const randomPrompts = [
`A charming skyscraper, nightfall`,
`An intense action figure of a disruptive shrew in an empty marsh, in the golden hour.`,
]
var generatedImage = ``;
var nextSubmitAllowedTime = 0;
function generateRandomPrompt() {
const sentences = [
`_TYPE_ of _SUBJECT_ _ACTION_ _ENVIRONMENT_, _EXTRAS_`,
]
const types = [
`An illustration`,
`A digital illustration`,
`A realistic illustration`,
`A artsy illustration`,
`A photograph`,
`A DSLR Picture`,
`A DSLR Photograph`,
`A digital painting`,
`A digital drawing`,
`A digital sketch`,
`A film photograph`,
];
const subjects = [
`A penguin`,
`A dog`,
`A man`,
`A woman`,
`A cat`,
`A kitten`,
`A dog`,
`A fish`,
`A squirrel`,
`A chipmunk`,
`A capybara`,
];
const action = [
`wearing a tuxedo`,
`wearing a top hat`,
`dressed with in a long skirt`,
`posing for a picture`,
`running`,
`sleeping on the left side`,
`looking at the viewer`,
`posing at the viewer`,
`posing at the viewer`,
]
const environments = [
`in a green forest`,
`in a white room`,
`with an interesting background`,
`with a studio background`,
`with a realistic background`,
`with a fantasy background`
];
const extras = [
`masterpiece, masterwork, artsy, creative, imagination, (realistic:1.1)`,
`masterpiece, masterwork, artsy, creative, imagination`,
`(realistic:1.1), DSLR, picture, photograph, polaroid`,
`film photography, kodak gold, kodak, 35mm film, film picture`,
]
return randomItem(sentences)
.replace(/_TYPE_/g, randomItem(types))
.replace(/_SUBJECT_/g, randomItem(subjects))
.replace(/_ACTION_/g, randomItem(action))
.replace(/_ENVIRONMENT_/g, randomItem(environments))
.replace(/_EXTRAS_/g, randomItem(extras))
}
function randomItem(items) { return items[Math.floor(Math.random()*items.length)]; }
async function handleImageGeneratorForm() {
/**
* @type {HTMLFormElement}
*/
const imageGeneratorForm = document.getElementById(`image-generator`);
if ( !!imageGeneratorForm ) {
// Randomize the prompt as a starting point
document.getElementById(`image_description`).value = `vaporwave, (vaporwave style:1.1), synthwave, 80's, aesthetic,`;
imageGeneratorForm.querySelector(`#random-prompt-button`).addEventListener( `click`, e => {
e.preventDefault();
document.getElementById(`image_description`).value = generateRandomPrompt();
})
imageGeneratorForm.querySelector(`#submit-image-button`).addEventListener( `click`, async (e) => {
// Cancel the default post, we wills end the data to an API endpoint
e.preventDefault();
if ( nextSubmitAllowedTime > new Date().getTime() ) {
const s = ((nextSubmitAllowedTime - new Date().getTime()) / 1000).toFixed(1);
alert(`Please wait ${s} more seconds before trying to submit`);
return;
}
if ( generatedImage == null || !!!generatedImage ) {
alert(`This image has already been submited`);
return;
}
// Disable the form
imageGeneratorForm.classList.add(`loading`);
imageGeneratorForm.querySelector(`button.btn-95`).setAttribute(`disabled`, true);
const fData = new FormData();
fData.set(`image_data`, generatedImage);
const response = await fetch(`/api/image/submit`, {
body: fData,
method: `POST`,
})
.catch( err => {
alert("Something went wrong with postiong your image, please try again later.");
console.error(err);
})
// Enable the form
imageGeneratorForm.classList.remove(`loading`);
imageGeneratorForm.querySelector(`button.btn-95`).removeAttribute(`disabled`);
generatedImage = null;
document.getElementById(`submit-image-button`).setAttribute(`disabled`, 123);
alert(`Your image has been submitted! Thanks for posting`);
nextSubmitAllowedTime = new Date().getTime() + (1000 * 15);
})
imageGeneratorForm.addEventListener(`submit`, async (e) => {
// Cancel the default post, we wills end the data to an API endpoint
e.preventDefault();
// Disable the form
imageGeneratorForm.classList.add(`loading`);
imageGeneratorForm.querySelector(`button.btn-95`).setAttribute(`disabled`, true);
const response = await fetch(`/api/image/generate`, {
body: new FormData(imageGeneratorForm),
method: `POST`,
})
.catch( err => {
alert("Something went wrong with generating the image, please try again later.");
console.error(err);
})
// Enable the form
imageGeneratorForm.classList.remove(`loading`);
imageGeneratorForm.querySelector(`button.btn-95`).removeAttribute(`disabled`);
/**
* @type {HTMLDivElement}
*/
const imageResultElement = imageGeneratorForm.querySelector(`#image-generation-result`);
const imgPlaceholder = imageResultElement.querySelector(`#tmp-img-placeholder`);
if ( !!imgPlaceholder ) {
imgPlaceholder.remove();
}
const r = await response.json();
imageResultElement.style.setProperty(`background-image`, `url('data:image/jpeg;charset=utf-8;base64,${r.images[0]}')`);
document.getElementById(`submit-image-button`).removeAttribute(`disabled`);
generatedImage = r.images[0];
})
}
}

0
public/js/vaporwave.js Normal file
View File

223
public/main.css Normal file
View File

@ -0,0 +1,223 @@
@import url("https://fonts.googleapis.com/css2?family=DotGothic16&family=Major+Mono+Display&family=Tinos:ital,wght@0,400;0,700;1,400;1,700&display=swap");
@import url("/public/css/buttons.css");
@import url("/public/css/vaporwave.css");
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
font-family: "DotGothic16", serif;
letter-spacing: 0.025em;
line-height: 1.5;
min-height: 100vh;
color: #393232;
background: linear-gradient(180deg, #2b1165 0%, #1a3a82 37%, #ab24b1 69%, #f54171 100%);
/* background: linear-gradient(180deg, #ab24b1 0%, #f54171 37%, #2b1165 69%, #1a3a82 100%); */
background-color: #2b1165;
overflow-x: hidden;
}
img {
display: block;
max-width: 100%;
}
article {
width: 90%;
max-width: 800px;
margin-left: auto;
margin-right: auto;
margin-top: 10vh;
margin-bottom: 10vh;
padding: 2em;
background-color: #c0c0c0;
border-left: 2px solid #eee;
border-top: 2px solid #eee;
border-right: 2px solid #444;
border-bottom: 2px solid #444;
font-size: 1rem;
position: relative;
}
article:after {
content: "x";
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
padding-bottom: 0.25em;
position: absolute;
top: 0.75em;
right: 0.75em;
width: 1.75em;
height: 1.75em;
background-color: #c0c0c0;
border-left: 2px solid #eee;
border-top: 2px solid #eee;
border-right: 2px solid #444;
border-bottom: 2px solid #444;
}
.headline {
font-size: 2.5em;
font-weight: 700;
}
.subhead {
font-size: 1.25em;
margin-top: 0.25em;
}
.article-meta {
display: flex;
flex-wrap: wrap;
flex-flow: column;
align-items: flex-start;
}
.article-meta div:first-of-type {
margin-left: 0.75em;
font-size: 0.875em;
margin-right: auto;
padding-right: 0.75em;
}
.article-meta div:last-of-type {
font-size: 0.875em;
}
aside {
padding: 1em;
border-right: 1px solid #eee;
border-bottom: 1px solid #eee;
border-left: 1px solid #444;
border-top: 1px solid #444;
clear: both;
}
.tag {
display: inline-block;
margin-right: 0.25em;
margin-top: 0.25em;
border-right: 1px solid #eee;
border-bottom: 1px solid #eee;
border-left: 1px solid #444;
border-top: 1px solid #444;
padding: 0.125em 0.375em;
text-transform: uppercase;
letter-spacing: 0.1em;
}
h2:not(.subhead) {
font-size: 1.5em;
font-weight: 700;
padding-bottom: 0.375em;
box-shadow: 0 1px 0 0 #444, 0 2px 0 0 #eee;
}
figure {
background-color: #c0c0c0;
box-shadow: 0 0 0 1px #eee, 0 0 0 2px #c0c0c0, 0 0 0 3px #444;
position: relative;
margin-bottom: 1.25em;
width: calc(100% + 1.5em + 2em);
@media (min-width: 800px) {
max-width: 50%;
}
}
figure:nth-of-type(odd) {
float: left;
margin-right: 1.5em;
margin-left: -3em;
}
figure:nth-of-type(even) {
float: right;
margin-left: 1.5em;
margin-right: -3em;
}
figure:after {
content: "";
top: 0;
left: 0;
right: 0;
bottom: 0;
display: block;
position: absolute;
background-image: linear-gradient(
to top,
rgba(#ff9100, 0.2) 0%,
rgba(#ffe630, 0.2) 60%
),
linear-gradient(20deg, rgba(#ff0, 0.5) 0%, rgba(#ff0, 0) 35%);
box-shadow: inset 0px 0px 100px rgba(0, 0, 20, 1);
z-index: 1;
}
figure figcaption {
font-family: "DotGothic16", monospace;
background-color: #8795e8;
box-shadow: inset 0 -1px 0 0 #020202;
color: #eee;
text-align: center;
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 0.25em;
font-size: 0.875em;
z-index: 2;
}
#image-generation-result {
border: 2px outset gray;
background-color: #c0c0c0;
padding: 2px;
margin-left: 1rem;
height: 512px;
width: 512px;
background-size: cover;
background-position: center;
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
}
.form-loader {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
width: calc(100% - 16px);
height: 100%;
background-size: 512px;
background-position: center;
background-image: url(/public/img/gifs/loading.gif);
position: absolute;
background-repeat: no-repeat;
background-color: #c0c0c0e8;
}
form:not(.loading) .form-loader {
display: none !important;
}
img.vaporwave-image {
max-width: 480px;
position: absolute;
z-index: 0;
}

130
templates/home/index.html Normal file
View File

@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Stable Diffusion * Lightning Talk</title>
<link rel="stylesheet"
href="/public/main.css">
</head>
<body>
<article style="z-index: 10">
<h1 class="headline"
style="margin-bottom: 0;">Stable Diffusion</h1>
<h2 class="subhead">The next generation of Image Generation</h2>
<div class="article-meta">
<img src="/public/img/Daniel.png"
width="80"
height="80"
style="border: 3px outset gray; background-color: #c0c0c0; padding: 2px;">
<div>
<p class="byline"
style="padding-bottom: 0; margin-bottom: 0;">by Daniel Dumitrascu</p>
<p class="dateline"
style="padding-top: 0; margin-top: 0;">May 4th, 2023</p>
<p class="article-tags">
</div>
<div>
<span class="tag">AI</span>
<span class="tag">image generation</span>
<span class="tag">tech</span>
</div>
</p>
</div>
<p>
Hey there! Thanks for hoppin' on the website, you're probably in my presentation over on zoom as well right now,
down below you will find a button to <b>generate images which you can then send over straight into the presentation live!</b>
</p>
<aside style="position: relative;">
<h3 style="margin: 0 auto;">Image Generator</h3>
<p>Describe the image you would like to create in the text area and hit "Generate" 😊</p>
<form id="image-generator"
style="display: flex; flex-flow: column;">
<div class="form-loader"></div>
<div style="display: flex; flex-flow: column; padding: 0; margin: 0; justify-content: center; align-items: center;">
<div id="image-generation-result">
<div id="tmp-img-placeholder"
style="display: flex; flex-flow: column; justify-content: center; align-items: center;">
<img src="/public/img/icons/broken.svg"
width="40"
alt=""
class="broken-image">
<span>no image found</span>
</div>
</div>
</div>
<div style="display: flex; flex-flow: column; padding: 0; margin: 0; margin-top: .5rem">
<textarea name="image_description"
id="image_description"
rows="10"
class="t95"></textarea>
<div style="display: flex; flex-flow: row; align-items: center; justify-content: center; margin: .5rem 0">
<button class="btn-95"
style="width: calc(50% - .5rem); margin-right: .5rem"
id="random-prompt-button">Random Prompt</button>
<button class="btn-95"
style="width: calc(50% - .5rem); margin-left: .5rem">Generate Image</button>
</div>
<button class="btn-95"
id="submit-image-button"
disabled>Submit Image</button>
</div>
</form>
</aside>
<br>
<br>
<br>
<br>
<figure>
<img src="/public/img/posters/1.jpeg"
alt="a smiling person in a pink hoodie, standing in front of a bright pink lighted arcade basketball game. " />
<figcaption>Samurai Ramen</figcaption>
</figure>
<p>First and foremost, what sets Stable Diffusion apart is its open-source nature. This means that anyone can access and modify the code to suit their specific needs. This makes it a great option for those who are interested in tinkering with code or developing their own algorithms.</p>
<h2>How it works</h2>
<p>Now, let's talk about how most image generation algorithms work. Essentially, these algorithms use deep neural networks to generate new images based on existing ones. These networks are trained on a large dataset of images and are able to learn the patterns and features that make up a specific type of image. Once trained, the network can generate new images by altering certain parameters or inputs.</p>
<p>In the case of Stable Diffusion, the software uses a type of algorithm called a diffusion model. This model works by iteratively adding noise to an initial image, gradually making it more and more "diffused" or blurry. This process is repeated multiple times, and the resulting images are then used to generate a new image.</p>
<p>But what really sets Stable Diffusion apart is its sampler. This feature allows users to "explore" the space of possible images by generating multiple images with slightly different parameters. This means that users can easily create a wide variety of images without having to manually adjust the parameters each time.</p>
<figure><img src="/public/img/posters/3.png" />
<figcaption>Superimposed woman on cat</figcaption>
</figure>
<h2>Prompts</h2>
<p>Finally, let's talk about prompts. In Stable Diffusion, prompts are used to provide the algorithm with specific guidance on what type of image to generate. These prompts can be anything from a written description of the desired image to a specific set of parameters. This means that users can easily generate images that match their specific needs or interests.</p>
<p>In conclusion, Stable Diffusion is a powerful image generation software that offers a wide range of features and capabilities. Its open-source nature makes it a great option for those who are interested in developing their own algorithms, and its diffusion model and sampler make it easy to generate a wide variety of images. So if you're looking for a versatile and customizable image generation software, be sure to check out Stable Diffusion!</p>
</article>
<!-- <div class="sun"></div> -->
<div class="overlay"></div>
<div class="overlay glitch"></div>
<img src="/public/img/vaporwave/1.png" class="vaporwave-image" style="left: calc(calc(50% - 240px) - 35rem); top: 240px;">
<img src="/public/img/vaporwave/2.png" class="vaporwave-image" style="right: calc(calc(50% - 240px) - 30rem); top: 1rem;">
<img src="/public/img/vaporwave/3.png" class="vaporwave-image" style="right: calc(calc(50% - 240px) - 25rem); top: 680px;">
<img src="/public/img/vaporwave/4.png" class="vaporwave-image" style="left: calc(calc(50% - 240px) - 35rem); top: calc(480px * 2);">
<img src="/public/img/vaporwave/5.png" class="vaporwave-image" style="right: calc(calc(50% - 240px) - 35rem); top: calc(480px * 3);">
<img src="/public/img/vaporwave/6.png" class="vaporwave-image" style="left: calc(calc(50% - 240px) - 45rem); top: calc(480px * 3.5);">
<img src="/public/img/vaporwave/7.png" class="vaporwave-image" style="right: calc(calc(50% - 240px) - 45rem); top: calc(480px * 4.25);">
<img src="/public/img/vaporwave/8.png" class="vaporwave-image" style="left: calc(calc(50% - 240px) - 45rem); top: calc(480px * 5.5);">
<img src="/public/img/vaporwave/9.png" class="vaporwave-image" style="right: calc(calc(50% - 240px) - 45rem); top: calc(480px * 5.4);">
<script src="/public/js/main.js"></script>
<script src="/public/js/vaporwave.js"></script>
</body>
</html>