Skip Navigation
webdev

Web Development

  • Best Framework for Web Development in 2025

    The world of web development is always changing, and keeping up with the latest frameworks is essential for developers who want to create modern, scalable, and user-friendly web applications. As we approach 2025, choosing the best framework for web development 2025 is important for developers and businesses aiming to stay competitive. This blog will discuss the most promising frameworks for web development that should be on your radar for the coming year.

    1. React.js

    React.js continues to be one of the most popular frameworks for web development. Created by Facebook, React allows developers to build interactive user interfaces with ease. Its component-based architecture makes it reusable and highly efficient for building large-scale applications. In 2025, React remains a top choice for developers looking to create fast and responsive web applications.

    2. Vue.js

    Vue.js is a lightweight and easy-to-learn framework for web development that has gained popularity due to its simplicity and flexibility. It is perfect for both small-scale projects and larger applications. The framework's ease of integration with existing projects and its clear documentation make it a great tool for developers looking for an alternative to more complex frameworks. In 2025, Vue.js is expected to continue being a preferred choice for developers seeking simplicity without sacrificing functionality.

    3. Angular

    Angular is a full-fledged framework for web development maintained by Google. Known for its strong features and robust architecture, Angular is ideal for creating dynamic, enterprise-scale web applications. While it has a steeper learning curve compared to React or Vue, it offers powerful tools for data binding, dependency injection, and routing. In 2025, Angular will remain crucial for developers who need a complete framework to handle complex projects efficiently.

    4. Next.js

    Next.js has risen as a preferred framework for web development for building server-rendered React applications and static websites. It offers great features like automatic static optimization, server-side rendering, and image optimization. These features make Next.js perfect for creating fast and SEO-friendly web applications. For businesses that provide web development services India, adopting Next.js can offer a competitive edge by delivering highly performant websites.

    5. Svelte

    Svelte has gained attention for its innovative approach to web development. Unlike traditional frameworks, Svelte compiles code at build time, resulting in minimal runtime overhead. This means faster applications with simpler code. For developers who want to move beyond conventional frameworks for web development, Svelte is a must-learn in 2025 due to its performance benefits and straightforward syntax.

    6. Django

    For those interested in server-side frameworks for web development, Django stands out as one of the best options for building powerful, secure web applications with Python. Django comes with built-in tools for user authentication, database management, and security, making it an all-in-one solution. Its emphasis on best practices helps developers create scalable and maintainable projects. As the use of Python grows, Django will continue to be a significant framework for web development in 2025.

    7. Ruby on Rails

    Ruby on Rails, or Rails, has been a reliable framework for web development for years, known for its simplicity and speed of development. It follows a convention-over-configuration approach, which means that developers can get projects off the ground quickly with less boilerplate code. For startups and small businesses looking to deploy applications rapidly, Ruby on Rails will remain a valuable tool in 2025.

    8. ASP.NET Core

    For those who prefer using Microsoft technologies, ASP.NET Core is an excellent framework for web development. It is cross-platform, open-source, and well-suited for creating high-performance web applications. With support for various programming languages, ASP.NET Core offers flexibility and scalability, making it a strong contender in 2025, especially for enterprise-level solutions.

    9. Laravel

    Laravel is a popular framework for web development using PHP, loved for its elegant syntax and comprehensive tools. It simplifies common tasks such as routing, authentication, and caching. Laravel is a great choice for developers who want to build robust, scalable web applications without a steep learning curve. In 2025, Laravel will continue to be a powerful tool for PHP developers and agencies providing web development services India.

    10. FastAPI

    For developers looking to create APIs with Python, FastAPI has become the go-to framework for web development. It is known for its speed, ease of use, and automatic documentation generation. FastAPI makes it easy to build high-performance, modern web APIs, and its use is expected to grow as more developers appreciate its efficiency in 2025.

    Conclusion

    Choosing the right framework for web development services India can make a big difference in creating fast, reliable, and easy-to-maintain web applications. As we approach 2025, the best framework depends on your project needs and your team's expertise. Key frameworks include:

    React.js – Flexible Vue.js – Simple Django – Powerful ASP.NET Core – Robust

    These frameworks can help developers and companies in India speed up their development process and create top-quality web applications. Learning these tools will help you stay competitive and build cutting-edge projects.

    0
  • Why is my 3d pointer not being displayed in my webapp?

    I'm designing a webapp that is supposed to be an AR environment on your phone, to be viewed with something like Google Cardboard, but I am having an issue that the segmentPointer object that is meant to appear when clicking on an object is not.

    I've checked the geometry displays correctly in a sandbox, and when I change it to a 3d object rather than shapeGeometry it does display, but I cannot figure out why it is not displaying how I want it to.

    The project is at https://voxelverse.jackgreenearth.org, and the code is quite long, but it is here to read in its totality below as it might need the whole context to discover the error. I've tried myself looking through the code, and I've tried searching the web and asking LLMs, but I couldn't figure it out, so please help me, fellow humans.

    Tap for code

    ``` "use strict";

    import \* as THREE from 'three';

    import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';

    \

    const loader = new GLTFLoader();

    const textureLoader = new THREE.TextureLoader();

    const manager = THREE.DefaultLoadingManager;

    \

    // Basic functions

    \

    function ls(id) {

    return(localStorage.getItem(id));

    };

    \

    function setLs(id, val) {

    localStorage.setItem(id, val);

    };

    \

    function byId(id) {

    return(document.getElementById(id));

    };

    \

    function bySel(sel) {

    return(document.querySelector(sel));

    };

    \

    function byClass(id) {

    return(document.getElementsByClassName(id));

    };

    \

    function toTitleCase(str) {

    return str.replace(

    /\w\S\*/g,

    function(txt) {

    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();

    }

    );

    };

    \

    function randInt(max) {

    return Math.floor(Math.random() \* (max));

    };

    \

    function getRandomFloat(min, max, decimals) {

    return(parseFloat((Math.random() \* (max - min) + min).toFixed(decimals)));

    };

    \

    function confine(value, min, max) {

    if(value < min) {

    return(min);

    } else if(value > max) {

    return(max);

    } else {

    return(value);

    };

    };

    \

    function wrap(value, min, max) {

    const range = max - min;

    \

    if(value < min) {

    return(wrap(value + range, min, max));

    } else if(value > max) {

    return(wrap(value - range, min, max));

    } else {

    return(value);

    };

    };

    \

    function removeFromArray(array, forDeletion) {

    return(array.filter(item => !forDeletion.includes(item)));

    };

    \

    function radToDeg(radians) {

    return radians \* (180 / PI);

    }

    \

    function range(start, stop, step = 1) {

    if (stop === undefined) {

    stop = start;

    start = 0

    }

    return Array.from({ length: (stop - start) / step }, (\_, i) => start + (i \* step));

    }

    \

    function between(variable, min, max, inclusive='min') {

    switch(inclusive) {

    case 'none':

    return((variable > min) && (variable < max));

    break;

    case 'both':

    return((variable >= min) && (variable <= max));

    break;

    case 'min':

    return((variable >= min) && (variable < max));

    break;

    case 'max':

    return((variable > min) && (variable <= max));

    break;

    }

    }

    \

    function download(data, filename, type) {

    var file = new Blob(\[data], {type: type});

    if (window\.navigator.msSaveOrOpenBlob) // IE10+

    window\.navigator.msSaveOrOpenBlob(file, filename);

    else { // Others

    var a = document.createElement("a"),

    url = URL.createObjectURL(file);

    a.href = url;

    a.download = filename;

    document.body.appendChild(a);

    a.click();

    setTimeout(function() {

    document.body.removeChild(a);

    window\.URL.revokeObjectURL(url);

    }, 0);

    };

    };

    \

    function log(text) {

    console.log(text);

    };

    \

    function distance2d(x1, y1, x2, y2) {

    return(Math.sqrt(

    (Math.abs(x1 - x2) \\ 2) +

    (Math.abs(y1 - y2) \\ 2)

    ));

    };

    \

    function distance3d(p1 = new THREE.Vector3(0, 0, 0), p2 = new THREE.Vector3(0, 0, 0)) {

    return(Math.sqrt((distance2d(p1.x, p1.y, p2.x, p2.y) \\ 2) + (Math.abs(p1.z - p2.z) \\ 2)));

    };

    \

    let totalElementsToLoad = 0;

    let numberOfElementsLoaded = 0;

    \

    function onAllElementsLoaded() {

    \

    }

    \

    function load(path, type, functionOnLoad) {

    totalElementsToLoad += 1;

    \

    if(type == 'html') {

    fetch(path)

    .then(response => response.text())

    .then(html => {

    let doc = new DOMParser().parseFromString(html, "text/html");

    \

    functionOnLoad(doc);

    \

    // If all elements to load have been loaded, execute the relevant function

    numberOfElementsLoaded += 1;

    if(numberOfElementsLoaded == totalElementsToLoad) {

    onAllElementsLoaded();

    }

    })

    .catch(error => {

    console.error(error);

    });

    } else if(type == 'json') {

    fetch(path)

    .then(response => response.json()) // parse the response as JSON

    .then(json => {

    functionOnLoad(json);

    \

    // If all elements to load have been loaded, execute the relevant function

    numberOfElementsLoaded += 1;

    if(numberOfElementsLoaded == totalElementsToLoad) {

    onAllElementsLoaded();

    }

    })

    .catch(error => {

    console.error(error);

    });

    }

    }

    \

    // Setup

    \

    const PI = 3.1415926535897932384626433832795028841971;

    \

    // Objects

    \

    let orientation = {

    'absolute': false,

    'alpha': 0,

    'beta': 0,

    'gamma': 0

    }

    \

    // vars

    const fps = 60;

    \

    let keysDown = \[];

    let pointerPosition = {'x': 0, 'y': 0, 'positions': \[{'clientX': 0, 'clientY': 0}], 'type': 'mouse'};

    \

    // Camera

    let cameraRotation = new THREE.Euler(0, 0, 0, 'YXZ');

    let cameraTargetRotation = {'x': 0, 'y': 0, 'z': 0};

    const cameraRotationSensitivity = 0.002;

    \

    // Other variables

    let logicInterval;

    \

    // Load default settings

    let defaultSettings;

    \

    load("/assets/json/default-settings.json", 'json', function(defset) {

    defaultSettings = defset;

    \

    // Create custom settings

    if(!Object.keys(localStorage).includes('settings')) {

    setLs('settings', JSON.stringify({}));

    };

    \

    onSettingsLoad();

    });

    \

    function settingURL(url, addValue=true) {

    return('children/' + url.split('/').join('/children/') + (addValue ? '/value' : ''));

    }

    \

    function customiseSetting(url, value) {

    url = settingURL(url).split('/');

    \

    let newSettings;

    \

    function recursiveSet(object, list, index, setTo) {

    // If the current component is the last one, assign the value

    if(index == list.length - 1) {

    object\[list\[index]] = setTo;

    return(object);

    } else {

    // Check if it already contains the value

    if(object.hasOwnProperty(list\[index])) {

    object\[list\[index]] = recursiveSet(object\[list\[index]], list, index + 1, setTo);

    } else {

    object\[list\[index]] = recursiveSet({}, list, index + 1, setTo);

    }

    return(object);

    }

    };

    \

    newSettings = recursiveSet(JSON.parse(ls('settings')), url, 0, value);

    \

    setLs('settings', JSON.stringify(newSettings));

    }

    \

    function getSetting(url, addValue) {

    url = settingURL(url, addValue).split('/');

    \

    function recursiveGet(object, list, index) {

    // If the current component is the last one, return the value

    if (index == list.length - 1) {

    return object\[list\[index]];

    } else {

    // Check if it contains the value

    if (object.hasOwnProperty(list\[index])) {

    return recursiveGet(object\[list\[index]], list, index + 1);

    } else {

    return null; // No such setting

    }

    }

    }

    \

    // Try to find it in local settings first, otherwise get it from defaultSettings

    const localGet = recursiveGet(JSON.parse(ls('settings')), url, 0);

    if(localGet == null) {

    return(recursiveGet(defaultSettings, url, 0));

    } else {

    return(localGet);

    }

    }

    \

    // First, lets define some functions

    // Rendering functions

    \

    // Thanks, https\://discourse.threejs.org/t/roundedrectangle-squircle/28645!

    function roundRectangleGeometry(w, h, r, s) { // width, height, radius corner, smoothness

    // helper const's

    const wi = w / 2 - r; // inner width

    const hi = h / 2 - r; // inner height

    const w2 = w / 2; // half width

    const h2 = h / 2; // half height

    const ul = r / w; // u left

    const ur = ( w - r ) / w; // u right

    const vl = r / h; // v low

    const vh = ( h - r ) / h; // v high

    let positions = \[

    -wi, -h2, 0, wi, -h2, 0, wi, h2, 0,

    -wi, -h2, 0, wi, h2, 0, -wi, h2, 0,

    -w2, -hi, 0, -wi, -hi, 0, -wi, hi, 0,

    -w2, -hi, 0, -wi, hi, 0, -w2, hi, 0,

    wi, -hi, 0, w2, -hi, 0, w2, hi, 0,

    wi, -hi, 0, w2, hi, 0, wi, hi, 0

    ];

    let uvs = \[

    ul, 0, ur, 0, ur, 1,

    ul, 0, ur, 1, ul, 1,

    0, vl, ul, vl, ul, vh,

    0, vl, ul, vh, 0, vh,

    ur, vl, 1, vl, 1, vh,

    ur, vl, 1, vh, ur, vh

    ];

    let phia = 0;

    let phib, xc, yc, uc, vc, cosa, sina, cosb, sinb;

    for (let i = 0; i < s \* 4; i ++) {

    phib = Math.PI \* 2 \* ( i + 1 ) / ( 4 \* s );

    cosa = Math.cos( phia );

    sina = Math.sin( phia );

    cosb = Math.cos( phib );

    sinb = Math.sin( phib );

    xc = i < s || i >= 3 \* s ? wi : - wi;

    yc = i < 2 \* s ? hi : -hi;

    positions.push( xc, yc, 0, xc + r \* cosa, yc + r \* sina, 0, xc + r \* cosb, yc + r \* sinb, 0 );

    uc = i < s || i >= 3 \* s ? ur : ul;

    vc = i < 2 \* s ? vh : vl;

    uvs.push( uc, vc, uc + ul \* cosa, vc + vl \* sina, uc + ul \* cosb, vc + vl \* sinb );

    phia = phib;

    }

    const geometry = new THREE.BufferGeometry( );

    geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );

    geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );

    return geometry;

    }

    \

    // Render

    function render() {

    requestAnimationFrame(render);

    leftRenderer.render(scene, leftCamera);

    rightRenderer.render(scene, rightCamera);

    \

    framesSoFar++;

    };

    \

    // Functions

    function setCameraRotation() {

    // Calculate drag

    cameraRotation.x = Number((cameraRotation.x + ((cameraTargetRotation.x - cameraRotation.x) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));

    cameraRotation.y = Number((cameraRotation.y + ((cameraTargetRotation.y - cameraRotation.y) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));

    cameraRotation.z = Number((cameraRotation.z + ((cameraTargetRotation.z - cameraRotation.z) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));

    // Update cameras

    for(let camera of \[leftCamera, rightCamera]) {

    camera.rotation.set(cameraRotation.x, cameraRotation.y, cameraRotation.z, 'YXZ');

    }

    \

    const eyeGap = getSetting('Quick Settings/Eye Gap');

    \

    // Set camera positions

    leftCamera.position.x = -1 \* eyeGap \* Math.sin(cameraRotation.y);

    leftCamera.position.z = -1 \* eyeGap \* Math.cos(cameraRotation.y);

    rightCamera.position.x = eyeGap \* Math.sin(cameraRotation.y);

    rightCamera.position.z = eyeGap \* Math.cos(cameraRotation.y);

    \

    byId('camera-target-rot-x').innerHTML = cameraTargetRotation.x.toFixed(2);

    byId('camera-target-rot-y').innerHTML = cameraTargetRotation.y.toFixed(2);

    byId('camera-target-rot-z').innerHTML = cameraTargetRotation.z.toFixed(2);

    byId('camera-rot-x').innerHTML = cameraRotation.x.toFixed(2);

    byId('camera-rot-y').innerHTML = cameraRotation.y.toFixed(2);

    byId('camera-rot-z').innerHTML = cameraRotation.z.toFixed(2);

    \

    byId('camera-left-rot-x').innerHTML = leftCamera.rotation.x.toFixed(2);

    byId('camera-left-rot-y').innerHTML = leftCamera.rotation.y.toFixed(2);

    byId('camera-left-rot-z').innerHTML = leftCamera.rotation.z.toFixed(2);

    }

    \

    function takeScreenshot() {

    downloadCanvasImage(document.getElementById('game-canvas'), gameName + ' screenshot');

    sendAlert('Screenshot Taken!', 'tick');

    };

    \

    function takePanorama() {

    const canvas = document.getElementById('game-canvas');

    const height = canvas.height;

    const width = canvas.width \* (360 / (camera.fov \* camera.aspect));

    let newCanvas = document.createElement('canvas');

    newCanvas.height = height;

    newCanvas.width = width;

    newCanvas.style.display = 'none';

    let context = newCanvas.getContext("2d");

    document.body.appendChild(newCanvas);

    for(let x = 0; x < width; x++) {

    // Rotate

    cameraRotation.y += ((2 \* PI) / width);

    let calculatedRotation = rotationToAbsolute(playerPosition, cameraRotation);

    camera.rotation.set(calculatedRotation.x, calculatedRotation.y, calculatedRotation.z, 'YXZ');

    renderer.render(scene, camera);

    const gl = renderer.getContext();

    // Get canvas data

    const pixelData = new Uint8ClampedArray(1 \* height \* 4);

    const reversedPixelData = new Uint8ClampedArray(1 \* height \* 4);

    gl.readPixels((canvas.width / 2), 0, 1, height, gl.RGBA, gl.UNSIGNED\_BYTE, pixelData);

    for (let i = 0; i < height; i++) {

    for (let j = 0; j < 4; j++) {

    reversedPixelData\[i\*4 + j] = pixelData\[(height - i - 1)\*4 + j];

    };

    };

    const imageData = new ImageData(reversedPixelData, 1, height);

    context.putImageData(imageData, x, 0);

    };

    downloadCanvasImage(newCanvas, gameName + ' panorama');

    newCanvas.remove();

    sendAlert('Panoramic screenshot taken!', 'tick');

    };

    \

    function setRotation(object, rotation) {

    object.rotation.set(rotation.x, rotation.y, rotation.z);

    };

    \

    function downloadCanvasImage(canvas, name) {

    let canvasImage = canvas.toDataURL('image/png');

    // this can be used to download any image from webpage to local disk

    let xhr = new XMLHttpRequest();

    xhr.responseType = 'blob';

    xhr.onload = function () {

    let a = document.createElement('a');

    a.href = window\.URL.createObjectURL(xhr.response);

    a.download = name;

    a.style.display = 'none';

    document.body.appendChild(a);

    a.click();

    a.remove();

    };

    xhr.open('GET', canvasImage); // This is to download the canvas image

    xhr.send();

    };

    \

    function xyToRealPosRot(x, y, distance) {

    let realX, realY, realZ, rotX, rotY, rotZ;

    \

    // Position is an object {x: x, y: y} x determines which face it will be on horizontally, and y determines if it will be on the top or the bottom

    // Beyond 400, x position wraps

    x = wrap(x, 0, 400);

    log('x before: ' + x)

    const horizontalFace = (x / 100) % 4;

    //rotY = (x / 400) \* (1) // horizontalFace);

    \

    // The top of the screen is y 100, the bottom is y -100, and the horizontals are between -50 and 50

    realY = confine(y, -100, 100);

    \

    // Calculate real position

    const unit = getSetting('Display/UI/Distance') / 50;

    \

    let forward = getSetting('Display/UI/Distance');

    \

    const bevel = getSetting('Display/UI/Bevel');

    \

    rotX = 0;

    \

    // If it is horizontal...

    if(between(y, -50 + bevel, 50 - bevel)) {

    realY = y;

    rotX = 0;

    } else if(y < -50 - bevel) {

    // If it is on the lower face

    realY = -50;

    forward = (y + 100) \* unit;

    rotX = -(PI / 2);

    } else if(y >= 50 + bevel) {

    // If it is on the upper face

    realY = 50;

    forward = (y - 100) \* unit;

    //side = unit \* (((x - 50) % 100) + 50);

    rotX = (PI / 2);

    } else if(between(y, -50 - bevel, -50 + bevel)) {

    // If it is on the lower bevel

    realY = -50 - ((y + 50) / 2);

    rotX = (PI / 4);

    } else if(between(y, 50 - bevel, 50 + bevel)) {

    // If it is on the upper bevel

    realY = 50 + ((y - 50) / 2) ;

    rotX = -(PI / 4);

    }

    \

    realY = realY \* unit;

    \

    let flip = false;

    \

    /\*if(

    (horizontalFace >= 0 && horizontalFace < 0.5) ||

    (horizontalFace >= 1.5 && horizontalFace < 2.5) ||

    (horizontalFace >= 3.5 && horizontalFace < 4)

    ) {

    flip = true;

    }\*/

    \

    let angle = (x / 400) \* (PI \* 2);

    realX = Math.sin(angle) \* forward;

    realZ = Math.cos(angle) \* forward;

    rotY = angle;

    log('rot y: ' + rotY)

    \

    log({

    'x': realX,

    'y': realY,

    'forward': forward,

    })

    \

    // Take distance into account

    realX \*= distance;

    realY \*= distance;

    realZ \*= distance;

    \

    return({

    'position': new THREE.Vector3(realX, realY, realZ),

    'rotation': new THREE.Euler(rotX, rotY, rotZ, 'YXZ'),

    'flip': flip

    });

    }

    \

    function addWidget({

    name = '',

    position = {'x': 0, 'y': 0},

    rotation = {'x': 0, 'y': 0, 'z': 0},

    distance = 1,

    size = {'x': 10, 'y': 10},

    radius = 3,

    shape = 'rRect',

    background = '#000000',

    opacity,

    textStyle = {

    'align': 'center',

    'weight': 0, // Range is 0 to 10

    'font': 'DINRoundPro,arial,sans-serif',

    'color': '#b0b0b0',

    'vertical-align': 'center',

    'font-size': 1 // Uses the same sizing system as the rest of the UI, so one unit of text is also one unit of object

    },

    textContent = '',

    onclick = function() {},

    onlongpress = function() {},

    onhover = function() {},

    onhoverexit = function() {},

    ontruehover = function() {}

    }) {

    const realPosRot = xyToRealPosRot(position.x, position.y, distance);

    log(realPosRot)

    const realPos = realPosRot.position;

    let realRot = realPosRot.rotation;

    \

    realRot.x += rotation.x;

    realRot.y += rotation.y;

    realRot.z = rotation.z;

    \

    // Calculate real size

    const unit = getSetting('Display/UI/Distance') / 100;

    \

    let width = unit \* size.x;

    let height = unit \* size.y;

    radius \*= unit;

    const scale = getSetting('Display/UI/Scale/General');

    width \*= scale;

    height \*= scale;

    radius \*= scale;

    \

    // Set mesh geometry

    let geometry;

    switch(shape) {

    case 'rRect':

    geometry = roundRectangleGeometry(width, height, radius, 10);

    break;

    case 'rect':

    geometry = new THREE.PlaneGeometry(width, height);

    break;

    case 'circle':

    geometry = new THREE.CircleGeometry((width + height) / 2, 32);

    break;

    }

    let material;

    \

    if(opacity == undefined) {

    opacity = 1;

    }

    \

    if(textContent == '') {

    if(background\[0] == '/') {

    loadTexture(background, function(texture) {

    material = new THREE.MeshBasicMaterial({

    map: texture,

    side: THREE.DoubleSide,

    opacity: opacity,

    transparent: true

    });

    onTextureLoad(material);

    })

    } else {

    material = new THREE.MeshBasicMaterial({

    color: background,

    side: THREE.DoubleSide,

    opacity: opacity,

    transparent: true

    });

    onTextureLoad(material);

    }

    } else {

    function prepareText(canvas) {

    // Proceed to prepare the canvas with the text

    ctx.font = \${textStyle\["font-size"]}em ${textStyle\["font"]}\;

    ctx.textAlign = textStyle\["align"];

    ctx.fillStyle = textStyle\["color"];

    ctx.fillText(textContent, 0, 0);

    // Compose the text onto the background

    const composedTexture = new THREE.CanvasTexture(canvas);

    \

    // Generate the material

    material = new THREE.MeshBasicMaterial({

    map: composedTexture,

    side: THREE.DoubleSide,

    transparent: true,

    alphaTest: 0.5

    });

    \

    onTextureLoad(material);

    }

    \

    // Initialize tmpcanvas only when needed

    const tmpcanvas = document.createElement('canvas');

    tmpcanvas.width = width;

    tmpcanvas.height = height;

    const ctx = tmpcanvas.getContext('2d');

    \ \

    // Fill the background first

    if (background\[0] == '/') {

    loadTexture(background, function(texture) {

    ctx.fillStyle = texture;

    ctx.fillRect(0, 0, width, height);

    \

    prepareText(tmpcanvas);

    })

    } else {

    ctx.fillStyle = background;

    ctx.fillRect(0, 0, width, height);

    \

    prepareText(tmpcanvas);

    }

    }

    function onTextureLoad(material) {

    // Create a mesh with the geometry and the material

    let mesh = new THREE.Mesh(geometry, material);

    \

    mesh.name = name;

    \

    mesh.position.set(realPos.x, realPos.y, realPos.z );

    mesh.rotation.set(realRot.x, realRot.y, realRot.z);

    \

    if(realPosRot.flip) {

    mesh.scale.x = -1;

    }

    \

    mesh.onclick = onclick;

    mesh.onlongpress = onlongpress;

    mesh.onhoverexit = onhoverexit;

    mesh.ontruehover = ontruehover;

    mesh.onchover = onhover;

    \

    scene.add(mesh);

    };

    }

    \

    function transitionWidget(name, property, newProperty, time, condition) {

    if(condition != null) {

    }

    }

    \

    // three.js Scene setup

    const scene = new THREE.Scene();

    \

    // three functions

    \

    function loadTexture(path, onload) {

    textureLoader.load(path, function (texture) {

    onload(texture);

    }, undefined, function (error) {

    console.error(error);

    });

    };

    \

    // Define objects to make them global, they will mostly be only added to the scene when settings are loaded

    let sun;

    let wallpaper;

    let cameraStream;

    let pointer;

    \

    let pointerMaterial = new THREE.MeshBasicMaterial({

    color: "hsl(0, 100%, 50%)",

    side: THREE.DoubleSide

    });

    \

    let segmentShape = new THREE.Shape();

    let segmentGeometry = new THREE.ShapeGeometry(segmentShape);

    let segmentPointer = new THREE.Mesh(segmentGeometry, pointerMaterial);

    segmentPointer.name = 'segmentPointer';

    \

    function setSegmentPointer(angle = 0, radius = 0.1, rotation = new THREE.Euler(0, 0, 0), clockwise=true) {

    let oldGeometry = segmentPointer.geometry;

    \

    let segmentShape = new THREE.Shape();

    segmentShape.moveTo(0, 0);

    segmentShape.arc(0, 0, radius, 0, angle, clockwise);

    segmentShape.lineTo(0, 0);

    \

    let extrudeSettings = {

    steps: 1,

    depth: 0.1,

    bevelEnabled: false

    };

    \

    let segmentGeometry = new THREE.ExtrudeGeometry(segmentShape, extrudeSettings);

    segmentPointer.geometry = segmentGeometry;

    \

    oldGeometry.dispose();

    \

    segmentPointer.rotation.set(rotation);

    }

    \

    // Camera stuff

    let cameraViewDistance;

    \

    // Setup cameras

    let leftCamera;

    let rightCamera;

    let leftRenderer;

    let rightRenderer;

    \

    function setRendererSize() {

    for(let renderer of \[leftRenderer, rightRenderer]) {

    let canvas = renderer.domElement;

    renderer.setSize(

    canvas.offsetWidth \* getSetting('Display/Anti-alias'),

    canvas.offsetHeight \* getSetting('Display/Anti-alias'),

    false

    );

    }

    }

    \

    function updateCameraAspectRatio() {-0.2

    for(let camera of \[leftCamera, rightCamera]) {

    let canvas = leftRenderer.domElement;

    camera.aspect = canvas.offsetWidth / canvas.offsetHeight;

    camera.updateProjectionMatrix();

    }

    }

    \

    // temp

    function startAssistant() {

    log('assisstant')

    }

    \

    // When settings are loaded, start settings stuff up

    function onSettingsLoad() {

    // Add sun

    sun = new THREE.PointLight(0xffffff, getSetting('Quick Settings/Brightness'));

    scene.add(sun);

    \

    // Pointers

    pointer = new THREE.Mesh(new THREE.IcosahedronGeometry(getSetting('Display/UI/Pointer/Size'), 1), pointerMaterial);

    pointer.name = 'pointer';

    scene.add(pointer);

    \

    pointerMaterial = new THREE.MeshBasicMaterial(

    {color: "hsl(" + (getSetting('Quick Settings/Theme Hue') + getSetting('Display/UI/Pointer/Hue Shift')) + ", 100%, 50%)"}

    );

    pointer.material = pointerMaterial;

    segmentPointer.material = pointerMaterial;

    \

    // Add wallpaper

    let wallpaperURL;

    if(getSetting('Display/UI/Wallpaper/Use Direct Link') == true) {

    wallpaperURL = getSetting('Display/UI/Wallpaper/Direct Link');

    } else {

    wallpaperURL = getSetting('Display/UI/Wallpaper/Wallpapers Folder') + '/' + getSetting('Display/UI/Wallpaper/Wallpaper');

    }

    \

    loadTexture(wallpaperURL, function(texture) {

    let material = new THREE.MeshStandardMaterial({

    map: texture,

    side: THREE.BackSide,

    transparent: true,

    opacity: getSetting('Display/UI/Wallpaper/Opacity')

    });

    \

    wallpaper = new THREE.Mesh(

    new THREE.IcosahedronGeometry(

    getSetting('Display/UI/Distance') \* getSetting('Display/UI/Wallpaper/Distance') \* getSetting('Display/UI/Testing Size Multiplier'), 4),

    material

    );

    wallpaper.scale.x = -1;

    wallpaper.name = "wallpaper";

    scene.add(wallpaper);

    });

    \

    // Setup cameras

    cameraViewDistance = getSetting('Display/UI/Distance') \* getSetting('Display/UI/Testing Size Multiplier') \* 2; // Keep this down to destroy lag

    leftCamera = new THREE.PerspectiveCamera(80, 1, 0.001, cameraViewDistance);

    rightCamera = new THREE.PerspectiveCamera(80, 1, 0.001, cameraViewDistance);

    \

    // Setup renderers

    leftRenderer = new THREE.WebGLRenderer({canvas: byId('left-canvas'), antialias: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true, alpha: true});

    rightRenderer = new THREE.WebGLRenderer({canvas: byId('right-canvas'), antialias: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true, alpha: true});

    \

    updateCameraAspectRatio();

    setRendererSize();

    \

    window\.addEventListener('resize', function() {

    updateCameraAspectRatio();

    setRendererSize();

    });

    \

    // Setup control center

    const baseY = getSetting('Display/Control Center/Switch To Bottom') ? -100 : 100;

    const backgroundFolder = getSetting('Display/Control Center/Icon Folder');

    function createTileGrid(scale, x, y, farness, tiles, name) {

    let counter = 0;

    log(tiles)

    addWidget({

    position: {'x': x, 'y': baseY + y},

    size: {'x': 3 \* scale, 'y': 3 \* scale},

    background: "hsl(" + getSetting('Quick Settings/Theme Hue') + ", 50%, 80%)",

    name: name + ' background',

    radius: 0.5 \* scale,

    onhover: openTileGroup(name)

    });

    for(let widgetY = 1; widgetY >= -1; widgetY--) {

    for(let widgetX = -1; widgetX <=1; widgetX++) {

    if(counter < tiles.length) {

    if(tiles\[counter].folder) {

    createTileGrid(scale / 3, (widgetX \* scale), (widgetY \* scale), farness \* 0.99, tiles\[counter].children);

    } else {

    log('scale' + scale)

    addWidget({

    position: {'x': x + (widgetX \* scale), 'y': baseY + y + (widgetY \* scale)},

    size: {'x': scale, 'y': scale},

    background: backgroundFolder + '/' + tiles\[counter].icon + '.svg',

    name: tiles\[counter].name,

    group: name,

    radius: scale / 3,

    distance: farness \* 0.99

    });

    log('added widget control center')

    };

    } else {

    break;

    };

    counter++;

    };

    if(counter >= tiles.length) {

    break;

    };

    };

    };

    \

    createTileGrid(

    getSetting('Display/UI/Scale/Control Center') \* 1.5,

    0,

    baseY,

    1,

    getSetting('Display/Control Center/Tiles'),

    getSetting('Display/Control Center/Tiles', false)\['name']

    );

    // Quick function

    let quickFunction = getSetting('Display/Control Center/Quick Function', false);

    \

    addWidget({

    position: {'x': 0, 'y': getSetting('Display/Control Center/Switch To Bottom') ? 100 : -100},

    background: '/assets/images/icons/control\_center/' + quickFunction.icon + '.svg',

    name: quickFunction.name,

    onclick: quickFunction.onclick

    });

    \

    // testing

    addWidget({

    position: {'x': 0, 'y': -50},

    background: '/assets/images/icons/control\_center/torch.svg',

    name: "torch"

    });

    addWidget({

    position: {'x': 200, 'y': 10},

    background: '/assets/images/icons/control\_center/screencast.svg',

    name: "screencast"

    });

    for(let i of range(16)) {

    addWidget({

    position: {'x': i \* 25, 'y': 0},

    background: 'hsl(' + getSetting('Quick Settings/Theme Hue') + ', 100%, ' + ((i / 16) \* 100) + '%)',

    name: "test" + i,

    textContent: '',//i.toString()

    'onclick': function() {

    log('click' + i);

    },

    'onhover': function() {

    log('hover' + i);

    }

    });

    }

    };

    \

    function updateSetting(url, value) {

    customiseSetting(url, value);

    \

    switch(url) {

    case 'Display/UI/Wallpaper/Opacity':

    wallpaper.material.opacity = value;

    break;

    };

    };

    \

    // Start

    \

    // Setup the camera stream

    function setupCameraStream() {

    function handleSuccess(stream) {

    cameraStream = document.createElement('video');

    cameraStream.style.transform = 'rotate(270deg)';

    cameraStream.srcObject = stream;

    cameraStream.play();

    \

    let texture = new THREE.VideoTexture(cameraStream);

    texture.minFilter = THREE.LinearFilter;

    texture.magFilter = THREE.LinearFilter;

    scene.background = texture;

    \

    customiseSetting('Display/UI/Wallpaper/Opacity', 0); // Temporary until GUI settings are introduced

    }

    \

    function handleError(error) {

    // Set wallpaper opacity to 1

    updateSetting('Display/UI/Wallpaper/Opacity', 1);

    \

    log('Unable to access the camera/webcam.');

    }

    \

    navigator.mediaDevices.getUserMedia({video: {facingMode: "environment"}})

    .then(handleSuccess)

    .catch(function(error) {

    if (error.name === 'OverconstrainedError') {

    // Fallback to default video settings

    navigator.mediaDevices.getUserMedia({video: true})

    .then(handleSuccess)

    .catch(handleError);

    } else {

    // Handle other errors

    handleError(error);

    }

    });

    };

    \

    // Fullscreen and pointer lock, request fullscreen mode for the element

    function openFullscreen(elem, then) {

    if (elem.requestFullscreen) {

    elem.requestFullscreen().then(then);

    } else if (elem.webkitRequestFullscreen) { /\* Safari \*/

    elem.webkitRequestFullscreen().then(then);

    } else if (elem.msRequestFullscreen) { /\* IE11 \*/

    elem.msRequestFullscreen().then(then);

    }

    }

    // Request pointer lock

    function requestPointerLock(myTargetElement) {

    const promise = myTargetElement.requestPointerLock({

    unadjustedMovement: true,

    });

    \

    if (!promise) {

    log("disabling mouse acceleration is not supported");

    return;

    }

    \

    return promise

    .then(() => log("pointer is locked"))

    .catch((error) => {

    if (error.name === "NotSupportedError") {

    // Some platforms may not support unadjusted movement.

    // You can request again a regular pointer lock.

    return myTargetElement.requestPointerLock();

    }

    });

    }

    \

    function lockPointer() {

    requestPointerLock(byId('body'));

    document.addEventListener("pointerlockchange", lockChangeAlert, false);

    };

    \

    function lockChangeAlert() {

    if (document.pointerLockElement === byId('body')) {

    document.addEventListener("mousemove", updatePosition, false);

    } else {

    document.removeEventListener("mousemove", updatePosition, false);

    }

    }

    \

    function updatePosition(e) {

    onLockedMouseMove(e.movementX, e.movementY);

    };

    \

    function fullscreenAndPointerLock() {

    openFullscreen(byId('body'), function() {

    lockPointer();

    });

    }

    \

    function permission() {

    // Check if the device supports deviceorientation and requestPermission

    if (typeof(DeviceMotionEvent) !== "undefined" && typeof(DeviceMotionEvent.requestPermission) === "function") {

    // Request permission

    DeviceMotionEvent.requestPermission()

    .then(response => {

    // Check the response

    if (response == "granted") {};

    })

    .catch(console.error); // Handle errors

    } else {

    // Device does not support deviceorientation

    log("DeviceOrientationEvent is not defined");

    }

    }

    \

    async function keepScreenAwake() {

    // Create a reference for the Wake Lock.

    let wakeLock = null;

    \

    // create an async function to request a wake lock

    try {

    wakeLock = await navigator.wakeLock.request("screen");

    log("Wake Lock is active!");

    } catch (err) {

    // The Wake Lock request has failed - usually system related, such as battery.

    log(\${err.name}, ${err.message}\);

    }

    }

    \

    let framesSoFar = 0;

    \

    function startFPSCount() {

    byId('logic-fps').innerHTML = fps.toString();

    \

    let renderFPSInterval = setInterval(function() {

    byId('render-fps').innerHTML = framesSoFar.toString();

    framesSoFar = 0;

    }, 1000);

    }

    \

    function start() {

    byId('loading-screen').style.display = 'none';

    setupCameraStream();

    startLogic();

    startFPSCount();

    render();

    permission();

    fullscreenAndPointerLock();

    keepScreenAwake();

    \

    byId('left-canvas').addEventListener('click', fullscreenAndPointerLock);

    byId('right-canvas').addEventListener('click', fullscreenAndPointerLock);

    };

    \

    // Loading

    byId('loading-bar').style.display = 'block';

    \

    manager.onProgress = function (url, itemsLoaded, itemsTotal) {

    //log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');

    \

    byId('loading-bar-fg').style.setProperty('--size', ((itemsLoaded / itemsTotal) \* 100) + '%');

    };

    \

    manager.onError = function (url) {

    log('There was an error loading ' + url);

    };

    \

    manager.onLoad = function ( ) {

    setTimeout(function() {

    byId('loading-bar').style.display = 'none';

    byId('play').style.display = 'block';

    }, 500);

    byId('play').addEventListener('click', start);

    };

    \

    function openTileGroup(group) {

    }

    \

    // Logic

    let pointerRaycast = new THREE.Raycaster();

    let previousIntersection = pointerRaycast.intersectObjects(scene.children);

    let clicking = false;

    \

    function logic() {

    // Set camera rotation

    if(cameraTargetRotation.x != cameraRotation.x || cameraTargetRotation.y != cameraRotation.y) {

    setCameraRotation();

    };

    \

    // Update pointer

    pointerRaycast.set(

    new THREE.Vector3(0, 0, 0),

    leftCamera.getWorldDirection(new THREE.Vector3())

    );

    \

    // Check if the pointer is itersecting with any object

    \

    const intersections = pointerRaycast.intersectObjects(scene.children);

    if (intersections.length > 0) {

    for(let intersection of intersections) {

    // If it's intersecting with itself, don't do anything

    if(intersection.object.name == 'pointer' || intersection.object.name == 'segmentPointer') {

    return;

    } else {

    // If it's intersecting with an object, copy that intersection's position, and start to click

    const point = intersections\[0].point;

    pointer.position.copy(point);

    \

    // Truehover

    if(Object.keys(intersection.object).includes('ontruehover')) {

    // Prevent hover being continuously trigggered

    if(previousIntersection.uuid != intersections\[0].uuid) {

    intersection.object.ontruehover();

    }

    }

    log('truehover')

    \

    // Start click after grace period, if object is clickable

    if(

    Object.keys(intersection.object).includes('onclick') ||

    Object.keys(intersection.object).includes('onhover') ||

    Object.keys(intersection.object).includes('onhoverexit') ||

    Object.keys(intersection.object).includes('onlongpress')

    ) {

    let gracePeriod = setTimeout(function() {

    // onhover

    if(Object.keys(intersection.object).includes('onhover')) {

    intersection.object.onhover();

    }

    log('hover')

    \

    // Start click

    if(Object.keys(intersection.object).includes('onclick') && (!clicking)) {

    clicking = true;

    \

    let fullness = 0;

    \

    // Manage pointers

    scene.add(segmentPointer);

    segmentPointer.position.copy(pointer);

    scene.remove(pointer);

    let startClick = setInterval(function() {

    fullness += (PI \* 2) / (fps \* getSetting('Input/Eye Click/Duration/Pre-click duration'));

    setSegmentPointer(

    fullness,

    getSetting('Display/UI/Pointer/Size') \* getSetting('Display/UI/Pointer/Clicking Size'),

    intersection.object.rotation

    );

    \

    byId('pointer-angle').innerHTML = fullness.toFixed(4);

    \

    // On forfeit

    let forfeitDistance = distance3d(point, pointerRaycast.intersectObjects(scene.children)\[0].point);

    if(forfeitDistance > getSetting('Input/Eye Click/Movement limit')) {

    log('forfeit ' + forfeitDistance)

    clearInterval(startClick);

    afterClick();

    }

    \

    if(fullness >= PI \* 2) {

    log('click')

    intersection.object.onclick();

    clearInterval(startClick);

    \

    if(Object.keys(intersection.object).includes('onlongpress')) {

    // Start longpress

    fullness = 0;

    let startLongPress = setInterval(function() {

    fullness += (PI \* 2) / (fps \* getSetting('Input/Eye Click/Duration/Long-press duration'));

    setSegmentPointer(

    fullness,

    getSetting('Display/UI/Pointer/Size') \* getSetting('Display/UI/Pointer/Clicking Size'),

    intersection.object.rotation,

    false

    );

    byId('pointer-angle').innerHTML = fullness.toFixed(4);

    \

    let forfeitDistance = distance3d(point, pointerRaycast.intersectObjects(scene.children)\[0].point);

    if(forfeitDistance > getSetting('Input/Eye Click/Movement limit')) {

    log('forfeitlongpress')

    clearInterval(startLongPress);

    afterClick();

    };

    \

    // On click

    if(fullness >= PI \* 2) {

    intersection.object.onlongpress();

    log('longpress')

    clearInterval(startLongPress);

    afterClick();

    }

    }, 1000 / fps);

    } else {

    afterClick();

    }

    }

    }, 1000 / fps);

    };

    }, getSetting('Input/Eye Click/Delayed hover duration') \* 1000)

    return;

    } else {

    afterClick();

    }

    \

    function afterClick() {

    // Update previous intersection

    previousIntersection = intersections;

    previousIntersection.intersection = intersection;

    \

    // Onhoverexit

    if(intersection.object.uuid != previousIntersection.intersection.object.uuid) {

    previousIntersection.object.onhoverexit();

    }

    \

    clicking = false;

    log('afterclick')

    \

    // Change back pointers

    scene.remove(segmentPointer);

    scene.add(pointer);

    \

    return;

    }

    }

    }

    };

    };

    \

    function startLogic() {

    logicInterval = setInterval(logic, 1000 / fps);

    };

    \

    function stopLogic() {

    clearInterval(logicInterval);

    cameraStream.pause();

    };

    \

    // Input

    function onLockedMouseMove(xMotion, yMotion) {

    cameraTargetRotation.x = confine(cameraTargetRotation.x - (yMotion \* cameraRotationSensitivity), -0.5 \* PI, 0.6 \* PI);

    if(wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI) != (cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity))) {

    cameraRotation.y = wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI);

    };

    cameraTargetRotation.y = wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI);

    setCameraRotation();

    };

    \

    // Setup buttons

    byId('toggle-debug').addEventListener('click', function(event) {

    if(byId('debug-menu').style.display == 'block') {

    byId('debug-menu').style.display = 'none';

    } else {

    byId('debug-menu').style.display = 'block';

    }

    });

    \

    // Keypress manager

    const keysToPreventDefault = \['alt', '/', 'f1', 'f2', 'f3'];

    \

    function putKeyDown(key) {

    if(!keysDown.includes(key.toLowerCase())) {

    keysDown.push(key.toLowerCase());

    };

    };

    \

    function putKeyUp(key) {

    keysDown = removeFromArray(keysDown, \[key.toLowerCase()]);

    };

    \

    document.addEventListener('keydown', function(e) {

    if(keysToPreventDefault.includes(e.key.toLowerCase())) {

    e.preventDefault();

    };

    putKeyDown(e.key);

    });

    \

    document.addEventListener('keyup', function(e) {

    putKeyUp(e.key);

    });

    \

    // Pointer position

    document.addEventListener('mousemove', function(e) {

    pointerPosition = {'x': e.clientX, 'y': e.clientY, 'positions': \[{'clientX': e.clientX, 'clientY': e.clientY}], 'type': 'mouse'};

    });

    \

    document.addEventListener('touchmove', function(e) {

    pointerPosition = {'x': e.touches\[0].clientX, 'y': e.touches\[0].clientY, 'positions': e.touches, 'type': 'touch'};

    });

    \

    // Gyrometer

    window\.addEventListener("deviceorientation", function(event) {

    orientation = {

    'absolute': event.absolute,

    'alpha': event.alpha,

    'beta': event.beta,

    'gamma': event.gamma

    };

    \

    byId('gyro-absolute').innerHTML = orientation.absolute;

    byId('gyro-alpha').innerHTML = orientation.alpha.toFixed(2);

    byId('gyro-beta').innerHTML = orientation.beta.toFixed(2);

    byId('gyro-gamma').innerHTML = orientation.gamma.toFixed(2);

    const theOrientation = (window\.offsetWidth > window\.offsetHeight) ? 'landscape' : 'portrait';

    \

    // If orientation is logged correctly

    if(!Object.values(orientation).includes(null)) {

    // Subtract 90deg if in portrait mode

    if(theOrientation == 'portrait') {

    orientation.alpha = wrap(orientation.alpha + 90, 0, 360);

    }

    \

    // Offset y

    const offsetY = 89.5; // I have no idea why this works

    \

    if(Math.abs(orientation.beta) < 100) {

    cameraTargetRotation.x = (-orientation.gamma \* (PI / 180) % 180) - offsetY;

    } else {

    cameraTargetRotation.x = (orientation.gamma \* (PI / 180) % 180) + offsetY;

    }

    cameraTargetRotation.y = orientation.alpha \* (PI / 180);

    cameraTargetRotation.z = (-orientation.beta \* (PI / 180)) + offsetY;

    \

    cameraRotation.x = cameraTargetRotation.x;

    cameraRotation.y = cameraTargetRotation.y;

    cameraRotation.z = cameraTargetRotation.z;

    \

    if(theOrientation == 'landscape') {

    }

    \

    setCameraRotation();

    };

    }, true); ```

    0
  • An Abridged History of Safari Showstoppers - Webventures
    webventures.rejh.nl An Abridged History of Safari Showstoppers - Webventures

    TL;DR: iOS Safari is more than an inconvenience for developers, it's the fundamental reason interoperability has been stymied in...

    An Abridged History of Safari Showstoppers - Webventures

    TL;DR: iOS Safari is more than an inconvenience for developers, it's the fundamental reason interoperability has been stymied in...

    2
  • [Solved] Having trouble with Jekyll on a new laptop: Help a casual web dev dum-dum out!

    Since I moved to a new laptop, my Jekyll setup seems to have become unusable. It appears the gems required by my site aren't found, or properly configured, even after a bundle install command that updated a bunch of them.

    Error messages on bundle exec jekyll serve: https://pastebin.com/gyg8YpMx

    Result of bundle install: https://pastebin.com/LKSstmJt

    I'm by no means a Jekyll or Ruby expert (barely a user), but I used to have a tolerably functional environment to update my site. Any help on getting that up and running again is much appreciated!

    Update 1:

    I managed to solve the SASS issue that @Kissaki@programming.dev pointed out. Now the bundler asks about a possible typo in a gallery plugin 😰

    See new error messages on bundle exec jekyll serve: https://pastebin.com/kk6HHv7Y I hope not every line in there is an error I have to fix...

    Update 2 (solved)

    Edited the RB plugin file according to the error message, and ...that was all? The site generates as in the olden days.

    Thank you all who chipped in with advice!

    4
  • [SOFTWARE REQUEST] Core for new API

    I am currently working on a fairly old website service for the company's internal needs. The stack is quite old - PHP 5.3. It so happened that company can't abandon it right now.

    ... In short, I received a task to develop a REST API for this service. It is clear that I cannot use the current stack. What can I use as a standalone core for the new API? The main thing for me is the speed of development and ease of learning. For example, I would like Bearer authorization / some rights model out of box.

    Something like Laravel, but separate instance of it just for the API seems a little overkill for me. What you can advise me? Any boilerplates / software recommendations are welcomed.

    PS: My database is MSSQL 2016, so it could be a problem.

    6
  • How to create functional webcomponents

    https://positive-intentions.com/blog/dim-functional-webcomponents/

    im investigating an idea i have about functional webcomponents after some experience with Lit.

    Lit is a nice lightweight UI framework, but i didnt like that it was using class-based components.

    Vue has a nice approach but i like working with the syntax that React used and i wondered if with webcomponents i could create a functional UI framework that didnt need to be transpiled.

    i think the article is already quite long, so i think i will create a separate one as a tutorial for it.

    note: im not trying to push "yet another ui framework", this is an investigation to see what is possible. this article is intended as educational.

    0
  • Always support compressed response in an API service
    ashishb.net Always support compressed response in an API service

    If you run any web service always enable support for serving compressed responses. It will save egress bandwidth costs for you. And, more importantly, for your users. Over time, the servers as well as client devices have become more powerful, so, compressing/decompressing data on the fly is cheap.

    0
  • Best way to add a blog to an existing, static website?

    I exported a Wordpress site as a static site and have been hosting that on Gitlab. I'd like to start updating the blog again and I'm wondering how to go about it.

    For the blog, I've been adding/coding the entries manually, which I still prefer to using Wordpress. Now I have someone who needs to take over the blog and I need something more simple for them.

    I've looked into DropInBlog ( https://dropinblog.com ) but it's way beyond our budget, so I've been thinking to either:

    • Give them git access and let them add a text file and image to a special directory when they want to post. Then I can have a script run a few times per hour which converts that into a blog post. I'd also need to update the blog index with my own code.

    • Let them use something RSS based with a nice interface and scrape that to generate the blog. Mastodon is one option, as is Wordpress. Ideally the blog they maintain would not be accessible to others on the web though. I don't want to split our SEO presence.

    Does anyone have a better suggestion? The website doesn't use a framework like Jekyll or any of those. It's just HTML, CSS and JS.

    4
  • Need to know about npm

    I'm following the odin project to learn web development. I had read about malicious packages in npm multiple times, so I avoided it until now. I'm on the webpack lesson now, and to use webpack, I need to install it using npm. I also see that it has many dependencies, and those dependencies will have other depenedencies and so on.

    Why is it like this? Is there any other tool like webpack that doesn't require npm? Or rather, can someone clarify how to properly use npm or link a guide that explains it? I have this kind of fear and reluctance about using npm after all the things I read.

    11
  • Reactivity and Reactive Programming [Blog Post]
    cosmicbyt.es Reactivity and Reactive Programming

    Nearly all modern browser UI frameworks boast Reactivity as a core feature. What is Reactivity all about? Is it useful or applicable outside of frontend development? what is the hype all about anyways?

    Reactivity and Reactive Programming

    Most modern JavaScript UI frameworks boast Reactivity, but have you ever wondered what that means exactly?

    In my opinion, Reactivity is largely responsible for making modern frontend development unintuitive to outsiders.

    This blog post explains what Reactivity is, and how it manifested in the frontend development world today.

    You might find this interesting if you're: a frontend dev unfamiliar with the concept, a non-frontend dev interested in frontend, or just curious what Reactivity is!

    0
  • Where to start with a forum or community site and what should I expect it to cost?

    I would like to create a site that that allows people to select a category and/or a sub-category and invite people to join and partake in something for a limited time period determined by the OP, but also allowing other users to suggest alternative options as a votable option.

    I have no idea where to start with any of this.

    Is it just forum software that I should be looking at, and could anyone recommend one if it is. Also how much should I be looking at to begin with?

    EDIT - Thanks everyone, all good options to look at. I am going to try a Lemmy Sub, as that seems to be the most favoured answer by users. Hopefully I will get to grips with it. 🙂

    6
  • Look Ma, No CDN!
    jade.ellis.link Look Ma, No CDN!

    CDNs are common - but are they really necessary? Is a single origin the performance pitfall many think it is?

    Look Ma, No CDN!
    14
  • Developers Rail Against JavaScript ‘Merchants of Complexity’
    thenewstack.io Developers Rail Against JavaScript ‘Merchants of Complexity’

    When both Pieter Levels and Alex Russell convincingly argue against using complex JavaScript frameworks, maybe frontend devs should listen.

    Developers Rail Against JavaScript ‘Merchants of Complexity’

    The rebelling against JavaScript frameworks continues. In the latest Lex Fridman interview, AI app developer Pieter Levels explained that he builds all his apps with vanilla HTML, PHP, a bit of JavaScript via jQuery, and SQLite. No fancy JavaScript frameworks, no modern programming languages, no Wasm.

    “I’m seeing a revival now,” said Levels, regarding PHP. “People are getting sick of frameworks. All the JavaScript frameworks are so… what do you call it, like [un]wieldy. It takes so much work to just maintain this code, and then it updates to a new version, you need to change everything. PHP just stays the same and works.”

    7
  • CSS Only 3D Robot

    Hello everyone! This is something I made back in university to test my CSS skills. I haven't practiced any HTML/CSS since then, but I thought I'd finally share this project with the community. Hope you like it!

    4
  • In a leaked recording, Amazon cloud chief tells employees that most developers could stop coding soon as AI takes over
    www.businessinsider.com In a leaked recording, Amazon cloud chief tells employees that most developers could stop coding soon as AI takes over

    Matt Garman sees a shift in software development as AI automates coding, telling staff to enhance product-management skills to stay competitive.

    In a leaked recording, Amazon cloud chief tells employees that most developers could stop coding soon as AI takes over

    > Matt Garman sees a shift in software development as AI automates coding, telling staff to enhance product-management skills to stay competitive.

    28
  • Create a `clip-path` animated ?

    Hi,

    I would like to use a rectangle that move (left to right) to reveal an element / image

    like this

    !

    The white box shall be the image to display

    But I'm already block at my svg animation

    svg <svg viewBox="0 0 265.135 68.642" xmlns="http://www.w3.org/2000/svg"> <g x="-55.790085" y="0.79151762"> <rect style="fill:#ffcc00;stroke-width:2.46513;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;stop-color:#000000" width="55.465603" height="151.60599" transform="rotate(45)" /> <animate attributeName="x" values="-55.790085;265" dur="5s" repeatCount="indefinite" /> </g> </svg>

    Because the rectangle is not moving :'(

    Any ideas ?

    Thanks.

    0
  • Nginx how enable CORS for multi origins ?

    Hi everyone,

    I would like to enable Cross-Origin Resource Sharing on my Nginx server. for few origins (cors requestor)/domains.

    I've found this article https://www.juannicolas.eu/how-to-set-up-nginx-cors-multiple-origins that is nice, but not complete and on my browser seem really hard to read due to the layout 🤮

    So I've opened a CodeBerg git repository for the good soul that want to perfect this piece of code the allow the most of use to use CORS with Nginx.

    https://codeberg.org/R1ckSanchez_C137/BestOfxxx/src/branch/main/Nginx/CORS_MultiDomains.py \ and \ https://codeberg.org/R1ckSanchez_C137/BestOfxxx/issues \

    If you don't want to create an account on codeberg feel free to post your code here !

    ```nginx server { # Server

    map "$http_origin" $cors { # map in Nginx is somewhat like a switch case in a programming language. default ''; #Seem to set $cors to '' empty string if none of the follwing rexeg match ? "~^https:\/\/([\w-\.]+\.)?example.com$" "$http_origin"; #regex domain match # ~ mean I suppose the string is RegEx ? # Need to come with a RegEx expression that match https://anything.example.com[optional ports and Query string ?X=Y] "~^https:\/\/([\w-\.]+\.)?example2.com$" "$http_origin"; #regex domain match }

    location /static {

    # if preflight request, we will cache it if ($request_method = 'OPTIONS') { add_header 'Access-Control-Max-Age' 1728000; #20 days add_header 'Content-Type' 'text/plain charset=UTF-8'; add_header 'Content-Length' 0; return 204; #https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204 }

    if ($cors != "") { add_header 'Access-Control-Allow-Origin' "$cors" always; # <-- Variable $cors add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Accept, Authorization, Cache-Control, Content-Type, DNT, If-Modified-Since, Keep-Alive, Origin, User-Agent, X-Requested-With' always;}

    # configuration lines...

    } }

    } ```

    0
  • Deno's Standard Library for JavaScript Finally Stabilized at v1 | 3min 5sec Video | Aug 8, 2024

    Video Description

    > Many programming languages have standard libraries. What about JavaScript? 🤔️ > > Deno's goal is to simplify programming, and part of that is to provide the JavaScript community with a carefully audited standard library (that works in Deno and Node) that offers utility functions for data manipulation, web-related logic, and more. We created the Deno Standard Library in 2021, and four years, 151 releases, and over 4k commits later, we're thrilled to finally announce that it's 30 modules are finally stabilized at v1. > > Learn more about the Deno Standard Library > > Read about our stabilization process for the library

    14
  • CSS finally adds vertical centering in 2024

    cross-posted from: https://lemmy.ca/post/27261082

    1
  • I just fixed a bug that was over 10 years old.

    I have a dust covered personal project that I created in ACTIONSCRIPT that I started porting to JS recently. I had an audio bug because the synth I created was too simple, so every time a note stopped playing it made an annoying click sound.

    Just fixed it by using attack/decay parameters in a gain node.

    It feels good. next steps feel a lot easier, and I can think about doing some more fun stuff.

    6
  • I need some clarification on ul li vs divs

    Hello,

    I'm a Sr Dev who mostly has done back-end work but I'm "dangerous enough" in front end frameworks to get things done in my job.

    I have another Sr Dev on my team who is ADAMANT on using ul/ol's everywhere. And I mean EVERYWHERE.

    Navigation menu items will get done as a list.

    Say I have a list of key value pairs or tags describing an item on a page, that's a list. If there are two sections on a page that's also a list. Even forms are built as lists of inputs and buttons. To the point where I'm positive if I told them to recreate the google front page I'm 100% they'd make a ul and a li for the image, another for the box and a separate li for the buttons.

    My frustration is that every piece of documentation regarding ordered lists and unordered lists are for literally listings out items as numbered or bulleted lists, not logically grouping things on a page. Also our code is littered with extra css to strip out the bullet points and numbers on a basic li item.

    I've worked on several projects and this is the first time I've ever seen lists so overused. Is this normal on some projects? It feels wrong but I don't know the exact terminology to use to explain why, given my inexperience in front end development.

    12
  • Shared VR space over P2P

    https://github.com/positive-intentions/chat

    the code related to the video is a faily basic implementation using BabylonJS. it can be found here.

    id like to see if i can get handpose-estimation to work well enough to be able to add to the BabylonJS render engine.

    im working on something i hope will work like the 8thwall demo here. i couldnt find an open-source alternative to this so i thought id have a crack at it myself. my progress so far is as described here. i dont have much experience in creating games or graphics, so any guidance/help/advice is appriciated.

    FAQ:

    • why should i use it? - its a proof-of-concept app. for testing and demo purposes only.
    • why create it? - it is a hobby project and i can be creative with the approach. its fun to work on.
    • what is it? - maybe this article helps.
    0
  • I won! Creating a top-notch game with Phaser in under 2 weeks
    reitgames.com I won! Creating a top-notch game with Phaser in under 2 weeks

    Ever wondered what it takes to transform a simple idea into an award-winning game in just two weeks? Come along as I share my challenging and exciting experience in this year's Gamedev.js game jam.

    I won! Creating a top-notch game with Phaser in under 2 weeks

    I hope this is okay. As a web dev who loves phaser, I love this stuff.

    If it's not, I apologize profusely.

    1
  • yhvr yhvr @lemm.ee
    Has anyone else had issues with domain registries randomly suspending a domain?

    Apologies if this is the wrong community. I spent some time searching for a good one, and this seemed to be fairly applicable.

    I've owned several domains over the years, but recently I purchased another one (goat.rest) to house a little side project I was working on. For about two weeks, everything was running fine, and then out of the blue the site disappeared. After some investigation, I figured out that the domain had been suspended by the registry, with seemingly no reason or course of action to get it back. I triple-checked, and although the TLD for the domain is intended for restaurants, it should be open for other uses too. The site wasn't spammy, explicit, or in any way content that would be cause for removal. I sent an email to the company that owns the TLD, and three days later the block was removed, and hours later I got an incredibly vague and short email stating as such.

    While the site was down, I did a little research and found a post where someone had a similar issue, but I haven't been able to find much else. Do registries just randomly, automatically suspend domains when they want to?

    I wrote a blog post going into a little more detail about the whole situation, but mainly I'm just really curious about the question I asked in the title. Am I just super unlucky to have this happen to me, or have other people experienced a similar situation?

    3
  • Do you need an email account with a web host if you use Gmail

    A friends business email goes through to his gmail account, but he also pays for an email account with his web host. Is it necessary to keep paying for this email account, does he need it if the email goes through to gmail?

    EDIT: What I know is, they both receive and send emails from @theirdomain.com via their Gmail. They have their domain registered with a seperate company than the web host and have access to their DNS records there. With the hosting company they pay for email in addition to their hosting plan.

    My friend wants to avoid the extra cost of the email account if possible, and I thought it wasn't required to have an email with your your webhost if setting up the DNS records properly, but not 100% sure. We wanted to get independent advice before proceeding, since the host has an incentive for my friend to stay on the email plan.

    10
  • Hosting wordpress in the UK

    Hi, I am looking for a WordPress host in the UK (or europe) and I have been researching them extensively, but would appreciate hearing people's experiences.

    My research has led me to a Kualo (www.kualo.com), they offer free SSL, unlimited bandwidth, have good reviews on TrustPilot, provide cloudflare CDN, and have a faily decent knowledge base.

    Has anyone used them and can offer personal experience? Or any other hosting recommendations? Thanks

    2
  • Applying the four principles of accessibility
    clearleft.com Applying the four principles of accessibility

    [Web Content Accessibility Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)—or WCAG—looks very daunting. It’s a lot to take in. It’s kind of overwhelming. It’s hard to know where to start.

    Applying the four principles of accessibility
    0
  • Interactive CSS grid tutorial
    ishadeed.com CSS Grid Areas

    A fresh look at the CSS grid template areas and how to take advantage of its full potential today.

    CSS Grid Areas

    > > > CSS Grid support has been widely available since March 2017 in all major browsers. Yet, here we are in 2024, and I still see few people using the grid template areas feature. > >

    > > > It’s no surprise that many avoid template areas as making sense of the grid is challenging enough. In this interactive article, I aim to shed light on this feature and, hopefully, convince you to use it more often. Once you see the simplicity and power of template areas, you may reach for them much more frequently. > >

    1
  • CSS Selectors
    css-tricks.com CSS Selectors | CSS-Tricks

    A complete guide covering all of the various methods we have to select elements in CSS and how to use them for applying styles.

    0
1 Active user