Club Command Line
How do I make an club command line?
A mini 2D virtual world with a command line made to be like the one in Minecraft. Manipulate JS-controlled chibis as well as your own (name in yellow) by using commands to change appearance and stats, teleport them somewhere else, or you can spawn more if you’d like! Since I wasn’t focused too much on AI for this demo, the JS-controlled chibis just move around randomly and send emojis. I have tab suggestions only working for required arguements.. What is a club command line? How do you make a club command line? This script and codes were developed by Jon Kantner on 17 August 2022, Wednesday.
Club Command Line - Script Codes HTML Codes
<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>Club Command Line</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/style.css">
</head>
<body> <canvas></canvas> <script src="js/index.js"></script>
</body>
</html>
Club Command Line - Script Codes CSS Codes
* { box-sizing: border-box; margin: 0; padding: 0;
}
body { font-family: Arial, sans-serif; margin: 0;
}
canvas, form, div { margin: auto;
}
canvas { display: block;
}
form { background-color: #048;
}
input, button { border: 0; vertical-align: middle;
}
input { appearance: none; -webkit-appearance: none; width: 75%;
}
button { color: #fff;
}
.send { background-color: #5b5; width: 15%;
}
.send:hover { background-color: #6c6;
}
.send:active { background-color: #4a4;
}
.send:disabled { opacity: 0.7;
}
.send:disabled:hover { background-color: #5b5;
}
.view-chat { background-color: #48c; width: 10%;
}
.view-chat:hover { background-color: #59d;
}
.view-chat:active { background-color: #37b;
}
.chat-log { background-color: rgba(0,0,0,0.5); color: #fff; display: none; flex-direction: column-reverse; overflow-y: scroll;
}
.chat-log span { line-height: 24px;
}
.help-text { color: #4f4;
}
.info-text { color: #ff4;
}
.error-text { color: #f44;
}
Club Command Line - Script Codes JS Codes
window.addEventListener("load", app);
function app() { var canvas = document.getElementsByTagName("canvas")[0], ctx = canvas.getContext("2d"), // canvas dimensions w = 568, h = 480, // scale, keep at 2 for best retina results s = 2; // set canvas dimensions with scale canvas.width = w * s; canvas.height = h * s; canvas.style.width = w + "px"; canvas.style.height = h + "px"; ctx.scale(s, s); /* Main app code */ // all artwork done by me :) var sprites = [ "http://jonkantner.com/experiments/vwc/grass.svg", "http://jonkantner.com/experiments/vwc/fountain.svg", "http://jonkantner.com/experiments/vwc/chibi_m.svg", "http://jonkantner.com/experiments/vwc/chibi_f.svg" ], images = []; for (var sp in sprites) { images.push(new Image()); images[sp].src = sprites[sp]; } var chatBar = { barH: 54, logH: 220, margin: 5, active: false, showLog: false, maxLines: 32, history: [], curHistoryItem: -1, curAutoCmpltCmd: -1, arg1pg: -1, arg2pg: -1, autoCmpltLvl: 0, logShow: function() { try { let log = document.querySelector(".chat-log").style, field = document.querySelector("input"); log.display = "flex"; this.active = true; this.showLog = true; field.focus(); } catch(err) { console.log("Chatbar must be created first in order to show the log"); } }, logHide: function() { try { let log = document.querySelector(".chat-log").style, field = document.querySelector("input"); log.display = "none"; this.active = false; this.showLog = false; field.blur(); } catch(err) { console.log("Chatbar must be created first in order to hide the log"); } }, logToggle: function() { if (this.showLog) { this.logHide(); } else { this.logShow(); } }, create: function() { let form = document.createElement("form"), field = document.createElement("input"), btn1 = document.createElement("button"), btn2 = document.createElement("button"), log = document.createElement("div"); // set up form elements and translate them to inside canvas form.action = ""; form.style.padding = this.margin + "px"; form.style.width = (canvas.width / s) + "px"; form.style.height = (this.barH) + "px"; form.style.transform = "translateY(" + (-this.barH) + "px)"; // text input field.type = "text"; field.style.fontSize = (this.barH*0.4) + "px"; field.style.height = (this.barH - this.margin*2) + "px"; field.style.padding = "0 " + this.margin + "px"; field.maxLength = 64; // send button btn1.className = "send"; btn1.style.fontSize = (this.barH*0.4) + "px"; btn1.style.height = (this.barH - this.margin*2) + "px"; btn1.disabled = "disabled"; // view chat button btn2.className = "view-chat"; btn2.style.fontSize = (this.barH*0.25) + "px"; btn2.style.height = (this.barH - this.margin*2) + "px"; // chat log log.className = "chat-log"; log.style.width = (canvas.width / s) + "px"; log.style.height = (this.logH) + "px"; log.style.transform = "translateY(" + (-this.barH*2 - this.logH) + "px)"; log.style.padding = this.margin + "px"; document.body.appendChild(form); document.body.appendChild(log); form.appendChild(field); form.appendChild(btn1); form.appendChild(btn2); btn1.appendChild(document.createTextNode("Send")); btn2.appendChild(document.createTextNode("View Chat")); } }, screenText = { text: "", color: "#fff", fontS: 16, timer: 3000, maxTime: 3000, fadeTime: 150, y: 0, h: 32, updateText: function(txt, y, h, c) { this.text = txt; this.timer = this.maxTime; this.y = y || 0; this.h = h || 32; this.color = c || "#fff"; } }, bubbleObj = function(text,w,x,y) { let minW = 35; this.text = text; this.w = w < minW ? minW : w; this.x = x; this.y = y; }, cmdObj = function(name, args, desc) { this.name = name; this.args = args || ""; this.desc = desc || ""; }, cmd = [ new cmdObj("clear","","clear chat"), new cmdObj("help","","get help menu"), new cmdObj("entityinfo","<name>","get details of entity"), new cmdObj("modentity","<name> <newname> [gender] [skin] [speed] [level]","modify entity"), new cmdObj("npc","<add|del> <name> [gender] [skin] [speed] [level] [<x> <y>]","add/delete NPC"), new cmdObj("tp","<name> <x> <y> or <name> <targetname>","teleport entity to new location"), new cmdObj("who","","get list of all entities") ], avatar = function(name, gender, skinTone, width, height, speed, frames, dir, x, y, lvl) { let nameLenLimit = 16; this.name = name.length > nameLenLimit ? name.substr(0,nameLenLimit) : name || "Anonymous"; this.gender = gender || 0; this.skinTone = skinTone || 0; this.w = width || 0; this.h = height || 0; this.speed = speed || 0; this.curFrame = 1; this.frames = frames || 1; this.dir = dir || null; this.isMoving = false; this.canMove = true; this.x = x || 0; this.y = y || 0; this.lvl = lvl || 0; this.lastMsg = ""; this.msgTimer = 0; this.msgMaxTime = 3000; this.msgFadeTime = 150; this.sendMsg = function(msg) { if (msg.length > 0) { let isCmd = false; chatBar.curAutoCmpltCmd = -1; chatBar.arg1pg = -1; chatBar.arg2pg = -1; // update last message if not a command if (msg[0] != "/") { this.lastMsg = msg; } else { isCmd = true; } let chatLog = document.querySelector(".chat-log"), newEntry = document.createElement("span"); /* if command, execute if used by player (whose level is always 0, and NPCs never send anything if they too are set at level 0) */ if (this.lvl === 0 && isCmd) { switch (msg.substr(1,msg.length - 1).split(" ")[0]) { // display help case "help": let helpHeading = "----- Help -----", cmdInfo = [], helpScrnTxt = ""; for (var c in cmd) { cmdInfo[c] = "/" + cmd[c].name + " " + cmd[c].args + (cmd[c].args.length > 0 ? " " : "") + "- " + cmd[c].desc; } newEntry.className = "help-text"; newEntry.appendChild(document.createTextNode(helpHeading)); helpScrnTxt += helpHeading + "%"; // show available commands for (var ci in cmdInfo) { newEntry.appendChild(document.createElement("br")); newEntry.appendChild(document.createTextNode(cmdInfo[ci])); helpScrnTxt += cmdInfo[ci] + "%"; } screenText.updateText(helpScrnTxt,h - chatBar.barH - (screenText.fontS*1.5*(cmdInfo.length)),screenText.fontS*2*(cmdInfo.length),"#4f4"); break; // clear chat case "clear": let clearMsg = "Chat cleared"; chatLog.innerHTML = ""; newEntry.appendChild(document.createTextNode(clearMsg)); screenText.updateText(clearMsg,h - chatBar.barH,screenText.fontS*2); break; // get entity details case "entityinfo": let eiArgs = msg.split(" "), eiTarget = eiArgs[1], eiSearch = worldObjs.find(s => s.name === eiTarget) || 0, eiFBLines = [], eiFeedback = ""; if (eiSearch !== 0 && eiTarget) { eiFBLines[0] = "----- " + eiSearch.name + " -----"; eiFBLines[1] = "Gender - " + (eiSearch.gender === 0 ? "male" : "female"); eiFBLines[2] = "Skin - " + eiSearch.skinTone; eiFBLines[3] = "Speed - " + eiSearch.speed; eiFBLines[4] = "Coordinates - " + Math.round(eiSearch.x) + "," + Math.round(eiSearch.y); eiFBLines[5] = "AI activity level - " + eiSearch.lvl; newEntry.className = "info-text"; for (var ei in eiFBLines) { newEntry.appendChild(document.createTextNode(eiFBLines[ei])); newEntry.appendChild(document.createElement("br")); eiFeedback += eiFBLines[ei] + "%"; } } else { eiFeedback = !eiArgs[1] ? "Please specify an entity." : "Entity not found"; newEntry.className = "error-text"; newEntry.appendChild(document.createTextNode(eiFeedback)); } let eiFBLen = eiFBLines.length > 0 ? eiFBLines.length : 1; screenText.updateText(eiFeedback,h - chatBar.barH - (screenText.fontS*1.5*(eiFBLen - 1)),screenText.fontS*2*(eiFBLen - 1),eiSearch !== 0 && eiArgs[1] ? "#ff4" : "#f44"); break; // modify entity case "modentity": let meArgs = msg.split(" "), meTarget = meArgs[1], meName = meArgs[2], meGender = meArgs[3], meSkin = meArgs[4], meSpeed = meArgs[5], meLevel = meArgs[6], meSearch = worldObjs.find(s => s.name === meTarget) || 0, meInvalid = false, meValidArgCt = 0, meFeedback = "Entity modified successfully"; if (meTarget) { if (meSearch !== 0) { if (meName) { meValidArgCt = 2; // check if new name isnt already used let meNameUsed = worldObjs.find(ne => ne.name === meName) || 0; if (meNameUsed === 0 || meTarget == meNameUsed.name) { if (meGender) { if ((meGender >= 0 && meGender <= 1) || meGender == "male" || meGender == "m" || meGender == "female" || meGender == "f") { ++meValidArgCt; if (meSkin) { if (meSkin >= 0 && meSkin <= 2) { ++meValidArgCt; if (meSpeed) { if (meSpeed >= 0 && meSpeed <= 9) { ++meValidArgCt; if (meLevel) { if (meLevel >= 0 && meLevel <= 20) { ++meValidArgCt; if (meTarget == player.name) { meInvalid = true; meFeedback = "Entity must be an NPC to modify AI activity level."; } } else { meInvalid = true; meFeedback = "Level must be between 0 and 20."; } } } else { meInvalid = true; meFeedback = "Speed must be between 0 and 9."; } } } else { meInvalid = true; meFeedback = "Skin must be between 0 and 2."; } } } else { meInvalid = true; meFeedback = "Gender must be 0 or 1. m(ale) and f(emale) are also valid."; } } } else { meInvalid = true; meFeedback = "'" + meNameUsed.name + "' has already been used. Please choose another name."; } } else { meInvalid = true; meFeedback = "Please give at least a new name to use"; } } else { meInvalid = true; meFeedback = "Entity does not exist."; } } else { meInvalid = true; meFeedback = "Usage: /modentity <name> <newname> [gender] [skin] [speed] [level]"; } if (!meInvalid) { let nameLenLimit = 16; meSearch.name = meName.length > nameLenLimit ? meName.substr(0,nameLenLimit) : meName; if (meValidArgCt >= 3) meSearch.gender = meGender == "male" || meGender == "m" ? 0 : (meGender == "female" || meGender == "f" ? 1 : meGender); if (meValidArgCt >= 4) meSearch.skinTone = meSkin; if (meValidArgCt >= 5) meSearch.speed = +meSpeed; if (meValidArgCt == 6) meSearch.lvl = +meLevel; } newEntry.className = !meInvalid ? "" : "error-text"; newEntry.appendChild(document.createTextNode(meFeedback)); screenText.updateText(meFeedback,h - chatBar.barH,screenText.fontS*2,!meInvalid ? "#fff" : "#f44"); break; // npc add/delete case "npc": let npcArgs = msg.split(" "), npcAction = npcArgs[1], npcName = npcArgs[2], npcGender = npcArgs[3], npcSkin = npcArgs[4], npcSpeed = npcArgs[5], npcLevel = npcArgs[6], npcX = npcArgs[7], npcY = npcArgs[8], npcFeedback = "NPC successfully added", npcInvalid = false, npcUsage = "Usage: /npc <add|del> <name> [gender] [skin] [speed] [level] [<x> <y>]"; if (npcAction == "add") { if (npcName) { let npcNameUsed = worldObjs.find(np => np.name === npcName) || 0; if (npcNameUsed === 0) { if (npcGender) { if ((npcGender >= 0 && npcGender <= 1) || npcGender == "male" || npcGender == "m" || npcGender == "female" || npcGender == "f") { if (npcSkin) { if (npcSkin >= 0 && npcSkin <= 2) { if (npcSpeed) { if (npcSpeed >= 0 && npcSpeed <= 9) { if (npcLevel) { if (npcLevel >= 0 || npcLevel <= 20) { if (npcX) { if (npcY) { if (!isNaN(npcX) && !isNaN(npcY)) { let xMax = canvas.offsetWidth; if (npcX < 0 && npcX > xMax && npcY < 0 && npcY > h - chatBar.barH) { npcInvalid = true; npcFeedback = "Placement is out of bounds. X limit is 0-" + xMax + ". Y limit is 0-" + (h - chatBar.barH) + "."; } } else { npcInvalid = true; npcFeedback = "Placement coordinates are invalid."; } } else { npcInvalid = true; npcFeedback = "Placement coordinates need both X and Y."; } } } else { npcInvalid = true; npcFeedback = "Level must be between 0 and 20."; } } } else { npcInvalid = true; npcFeedback = "Speed must be between 0 and 9."; } } } else { npcInvalid = true; npcFeedback = "Skin must be between 0 and 2."; } } } else { npcInvalid = true; npcFeedback = "Gender must be 0 or 1. m(ale) and f(emale) are also valid."; } } } else { npcInvalid = true; npcFeedback = "'" + npcNameUsed.name + "' has already been used. Please choose another name."; } } else { npcInvalid = true; npcFeedback = "Please choose at least a name for the NPC."; } if (!npcInvalid) { let aGender = (npcGender == "male" || npcGender == "m" ? 0 : (npcGender == "female" || npcGender == "f" ? 1 : npcGender)) || 0, aSkin = npcSkin || 0, aSpeed = npcSpeed || 3, aLevel = npcLevel || 8, aX = npcX || player.x, aY = npcY || player.y, newNPC = new avatar(npcName,aGender,aSkin,30,60,+aSpeed,28,2,+aX,+aY,+aLevel); npcs.push(newNPC); worldObjs.push(npcs[npcs.length - 1]); } } else if (npcAction == "del") { if (npcName) { let npcSearch = npcs.find(s => s.name === npcName) || 0; if (npcSearch !== 0) { for (var n in npcs) { if (npcs[n].name == npcSearch.name) { npcs.splice(n,1); } } for (var w in worldObjs) { if (worldObjs[w].name == npcSearch.name) { worldObjs.splice(w,1); } } npcFeedback = "NPC successfully deleted"; } else { npcInvalid = true; npcFeedback = "Could not find that NPC to delete"; } } else { npcInvalid = true; npcFeedback = "Please specify an NPC to delete."; } } else { npcInvalid = true; npcFeedback = npcUsage; } newEntry.className = !npcInvalid ? "" : "error-text"; newEntry.appendChild(document.createTextNode(npcFeedback)); screenText.updateText(npcFeedback,h - chatBar.barH,screenText.fontS*2,!npcInvalid ? "#fff" : "#f44"); break; // teleport case "tp": let tpArgs = msg.split(" "), tpEntity = tpArgs[1], tpAfterEn = tpArgs[2], enSearch = worldObjs.find(s => s.name === tpEntity) || 0, rel = "~", tpOK = false, tpFeedback = "", tpUsage = "Usage: /tp <name> <x> <y> or <name> <targetname>"; if (tpAfterEn) { if (isNaN(tpAfterEn) && tpAfterEn[0] != rel) { let tarEntity = tpAfterEn, tEnSearch = worldObjs.find(ts => ts.name === tarEntity) || 0, bothNames = tpEntity && tarEntity ? true : false; tpOK = bothNames && enSearch !== 0 && tEnSearch !== 0 ? true : false; tpFeedback = bothNames ? (enSearch !== 0 ? (tEnSearch !== 0 ? "Teleported " + tpEntity + " to " + tarEntity : "Target entity does not exist") : "Entity does not exist") : tpUsage; if (tpOK) { enSearch.x = tEnSearch.x; enSearch.y = tEnSearch.y; } } else { let tpX = tpAfterEn, tpY = tpArgs[3]; if (tpX && tpY) { // convert relative positions to regular if (tpX[0] == rel) { tpX = +tpX.substr(1,tpX.length - 1) + enSearch.x; } else { tpX = +tpX; } if (tpY[0] == rel) { tpY = +tpY.substr(1,tpY.length - 1) + enSearch.y; } else { tpY = +tpY; } } let cw = canvas.offsetWidth, allValues = tpEntity && (tpX || tpX === 0) && (tpY || tpY === 0) ? true : false, wthnScrn = tpX >= 0 && tpX <= cw && tpY >= 0 && tpY <= h - chatBar.barH ? true : false; tpOK = enSearch !== 0 && allValues && wthnScrn ? true : false, tpFeedback = allValues ? (enSearch !== 0 ? (wthnScrn ? "Teleported " + tpEntity + " to " + Math.round(tpX) + "," + Math.round(tpY) : "Coordinates are out of bounds. X limit is 0-" + cw + ". Y limit is 0-" + (h - chatBar.barH) + ".") : "Entity does not exist.") : tpUsage; if (tpOK) { enSearch.x = tpX; enSearch.y = tpY; } } } else { tpFeedback = tpUsage; } newEntry.className = tpOK ? "" : "error-text"; newEntry.appendChild(document.createTextNode(tpFeedback)); screenText.updateText(tpFeedback,h - chatBar.barH,screenText.fontS*2,tpOK ? "#fff" : "#f44"); break; // get list of all entities in alphabetical order case "who": let getEntities = [player.name], displayEntNames = "Entity list: "; for (var ge in npcs) { ge = +ge + 1; getEntities[ge] = npcs[ge - 1].name; } getEntities.sort(function(a, b){ if (a.toLowerCase() < b.toLowerCase()) return -1; if (a.toLowerCase() > b.toLowerCase()) return 1; return 0; }); for (var de in getEntities) { de = +de; displayEntNames += (de > 0 ? ", " : "") + getEntities[de]; } newEntry.appendChild(document.createTextNode(displayEntNames)); screenText.updateText(displayEntNames,h - chatBar.barH,screenText.fontS*2); break; // invalid command default: let cmdErr = "Invalid command. See /help for a list of available commands."; newEntry.className = "error-text"; newEntry.appendChild(document.createTextNode(cmdErr)); screenText.updateText(cmdErr,h - chatBar.barH,screenText.fontS*2,"#f44"); break; } } else { this.msgTimer = this.msgMaxTime; newEntry.appendChild(document.createTextNode(this.name + ": " + this.lastMsg)); } // add new line chatLog.insertBefore(newEntry, chatLog.childNodes[0]); // cut off oldest line if at max lines allowed if (chatLog.childNodes.length > chatBar.maxLines) { chatLog.removeChild(chatLog.getElementsByTagName("span")[chatBar.maxLines]); } } }; }, structure = function(width, height, x, y, backArea, img, isAnim, frames) { this.w = width; this.h = height; this.x = x; this.y = y; this.backArea = backArea || 0; this.img = img || null; this.isAnim = img && isAnim ? (typeof isAnim == "boolean" ? true : false) : false; this.frames = frames || 1; this.curFrame = 1; }, randNum = function(min, max) { return Math.floor(Math.random() * (max - min)) + min; }, collision = function(a, b) { // top hits bottom, bottom hits top, left hits right, right hits left if ( ((a.y < b.y + b.h + 6 - b.backArea && a.y > b.y) || (a.y > b.y && a.y < b.y + b.h - b.backArea)) && ((a.x + a.w > b.x && a.x + a.w < b.x + b.w) || (a.x < b.x + b.w && a.x > b.x)) ) { return true; } else { return false; } }, findCllsn = function(a, b) { for (var bi in b) { if (collision(a, b[bi]) && Array.isArray(b)) { return true; } } }, player = new avatar("Player",0,0,30,60,3,28,2,w/2 - 15,h*0.8 - chatBar.barH), npcs = [], structures = [ new structure(w,50,0,-40), new structure(10,h - chatBar.barH - 10,0,10), new structure(10,h - chatBar.barH - 10,w - 10,10), new structure(300,200,w/2 - 150,100,70,images[1],true,12) ], worldObjs = [], createNPCs = function() { let nameObj = function(name,gender) { this.name = name; this.gender = gender; }, npcNames = [ new nameObj("Alice", 1), new nameObj("Jack", 0), new nameObj("Jill", 1) ], // to be same as players avatarW = 30, avatarH = 60; for (var npcn in npcNames) { let chooseSkin = randNum(0,3), placeX = randNum(0,w - avatarW), placeY = randNum(avatarH,h - chatBar.barH - avatarH); npcs[npcn] = new avatar(npcNames[npcn].name,npcNames[npcn].gender,chooseSkin,avatarW,avatarH,3,28,2,placeX,placeY,8); // relocate to player location if ended up inside structure if (findCllsn(npcs[npcn],structures)) { npcs[npcn].x = player.x; npcs[npcn].y = player.y; } } }, control = function(avatar, e) { // avatar.dir values: 0 = up, 1 = right, 2 = down, 3 = left if (e && !chatBar.active) { avatar.isMoving = true; avatar.canMove = true; switch (e.keyCode) { case 37: avatar.dir = 3; break; case 38: avatar.dir = 0; break; case 39: avatar.dir = 1; break; case 40: avatar.dir = 2; break; default: avatar.canMove = false; break; } } }, stopControl = function(avatar) { avatar.isMoving = false; }, avatarSpriteLoop = function(avatar) { if (avatar.curFrame == avatar.frames) { avatar.curFrame = 1; } else { ++avatar.curFrame; } }, moveAvatar = function(avatar) { if (avatar.isMoving && avatar.canMove) { switch (avatar.dir) { case 3: avatar.x -= avatar.speed; // collision with right side of structure, collisions apply to walls as well if (findCllsn(avatar,structures) || avatar.x < 0) { avatar.x += avatar.speed; avatar.curFrame = 1; } else { avatarSpriteLoop(avatar); } break; case 0: avatar.y -= avatar.speed; // bottom side if (findCllsn(avatar,structures) || avatar.y < 0) { avatar.y += avatar.speed; avatar.curFrame = 1; } else { avatarSpriteLoop(avatar); } break; case 1: avatar.x += avatar.speed; // left side if (findCllsn(avatar,structures) || avatar.x + avatar.w > w) { avatar.x -= avatar.speed; avatar.curFrame = 1; } else { avatarSpriteLoop(avatar); } break; case 2: avatar.y += avatar.speed; // top side if (findCllsn(avatar,structures) || avatar.y + avatar.h > h) { avatar.y -= avatar.speed; avatar.curFrame = 1; } else { avatarSpriteLoop(avatar); } break; default: break; } } else { avatar.curFrame = 1; } }, npcAI = function(npc) { if (npc.lvl > 0) { npc.isMoving = randNum(0,npc.lvl + 1) === 0 ? false : true; // just like player, NPCs can chat if not moving if (npc.isMoving) { npc.dir = randNum(0,4); } else { let msgs = ["
Developer | Jon Kantner |
Username | jkantner |
Uploaded | August 17, 2022 |
Rating | 4 |
Size | 12,560 Kb |
Views | 34,408 |
Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!
Name | Size |
SM64 Endless Staircase | 7,342 Kb |
Black Hole Illusion | 4,153 Kb |
Smash Ball | 4,246 Kb |
Isometric Japanese Castle | 15,012 Kb |
Busy Cursors | 11,192 Kb |
Roll-Up US Constitution | 3,532 Kb |
How the New Sublime Text Logo Was Made | 3,213 Kb |
Responsive Morphing Apple Product | 3,307 Kb |
3D Half Word and Shadow | 4,565 Kb |
Flying Windows Logo | 3,047 Kb |
Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!
Name | Username | Size |
CSS3 Latte Art Logo | Esambino | 2,036 Kb |
A Pen by Kevin | Kevinkenger | 2,642 Kb |
Save for later... | Victorfreire | 1,359 Kb |
Getting Started | Viblast | 1,500 Kb |
Shopping cart | Andiio | 6,581 Kb |
Fireworks Show | Arianalynn | 3,048 Kb |
The Fantastic Mr Fox | MalZiiirA | 10,435 Kb |
Menubar compass mixin | Michaelparenteau | 4,925 Kb |
Nice responsive team page | Infomiho | 3,139 Kb |
Header Line Issue | Charlie-volpe | 1,768 Kb |
Surf anonymously, prevent hackers from acquiring your IP address, send anonymous email, and encrypt your Internet connection. High speed, ultra secure, and easy to use. Instant setup. Hide Your IP Now!