← Back to Models
Bedside clip-on shelf tray for bed. Customizable

Bedside clip-on shelf tray for bed. Customizable

by Mach3 ·

Fully customizable. Change 'bed wall thickness' param to fit your bed wall. `total shelf length` 195mm is printed fine on A1 mini if positioned 45deg on the print bed (brims are recommended for better adhesion).

Interactive Preview & Customizer

3D preview with browser-based OpenSCAD rendering on parameter changes.

Loading previewLoading preview...

Total horizontal shelf length/depth

step 1

Width of the shelf along the bed wall (extrusion length)

step 1

Thickness of the bed headboard/wall

step 1

General wall thickness for structural strength

step 1

Thickness of the shelf plate and top connector

step 1

Depth of the hook clamp on both sides (making both walls the same height)

step 0.5

Chamfer size for the bottom tips of the clamp walls (flat-bottomed chamfer)

step 0.1

Toggle to enable chamfers in the inner corners of the hook slot

Thickness of the support brace

step 0.1

Horizontal extension of the brace under the shelf

step 0.5

Vertical extension of the brace on the middle wall

step 0.5

Chamfer size for the support brace angles

step 0.1

Toggle to enable the recessed tray (raised lip). Set to false for a flat top shelf.

Toggle to make the shelf outer corners chamfered (45 deg) or square (90 deg)

Chamfer size for the outer shelf corners

step 0.1

Width of the raised lip around the shelf

step 0.1

Depth of the recess (tray pocket)

step 0.1

Bevel/chamfer width for the sloped inner edge of the lip

step 0.1

Chamfer size for the shelf outer edge profile (flat-front chamfer)

step 0.1
WASM compiler loads on first parameter change.
Download

Screenshots

Model screenshot

Prompt

Create clip-on shelf tray for bed.

OpenSCAD Code

// Parametric Bed Shelf Design - v21
// Optimized for printing on its side without supports on a Bambu Lab A1 Mini (180x180x180mm)

/* [Main Dimensions] */
// Total horizontal shelf length/depth
total_shelf_length = 195; // [100:1:280]
// Width of the shelf along the bed wall (extrusion length)
shelf_width = 175; // [50:1:280]
// Thickness of the bed headboard/wall
bed_wall_thickness = 37; // [5:1:100]
// General wall thickness for structural strength
wall_thickness = 6; // [1:1:15]
// Thickness of the shelf plate and top connector
shelf_thickness = 8; // [1:1:15]

/* [Hook/Clamp Dimensions] */
// Depth of the hook clamp on both sides (making both walls the same height)
hook_depth = 65; 
// Chamfer size for the bottom tips of the clamp walls (flat-bottomed chamfer)
wall_chamfer = 2;
// Toggle to enable chamfers in the inner corners of the hook slot
enable_hook_chamfer = false;

/* [Support Brace] */
// Thickness of the support brace
brace_thickness = 6;
// Horizontal extension of the brace under the shelf
brace_depth = 60;
// Vertical extension of the brace on the middle wall
brace_height = 45;
// Chamfer size for the support brace angles
brace_chamfer = 2;

/* [Tray/Recess Options] */
// Toggle to enable the recessed tray (raised lip). Set to false for a flat top shelf.
enable_tray = true;
// Toggle to make the shelf outer corners chamfered (45 deg) or square (90 deg)
chamfer_shelf_corners = true;
// Chamfer size for the outer shelf corners
corner_radius = 12;
// Width of the raised lip around the shelf
lip_width = 4;
// Depth of the recess (tray pocket)
recess_depth = 2;
// Bevel/chamfer width for the sloped inner edge of the lip
recess_bevel_width = 2;
// Chamfer size for the shelf outer edge profile (flat-front chamfer)
fillet_radius = 2;

/* [Calculated Parameters] */
hook_total_width = bed_wall_thickness + 2 * wall_thickness;
shelf_projection = total_shelf_length - hook_total_width;

module profile2D() {
    // 1. Shelf plate with chamfered outer edge (flat-front chamfer)
    polygon([
        [0, 0],
        [-shelf_projection + fillet_radius, 0],
        [-shelf_projection, -fillet_radius],
        [-shelf_projection, -shelf_thickness + fillet_radius],
        [-shelf_projection + fillet_radius, -shelf_thickness],
        [0, -shelf_thickness]
    ]);

    // 2. Middle wall (extending up to Y = 0, with flat-bottomed chamfer)
    polygon([
        [0, 0],
        [wall_thickness, 0],
        [wall_thickness, -hook_depth + wall_chamfer],
        [wall_thickness - wall_chamfer, -hook_depth],
        [wall_chamfer, -hook_depth],
        [0, -hook_depth + wall_chamfer]
    ]);

    // 3. Top connector (thickness controlled by shelf_thickness)
    translate([wall_thickness, -shelf_thickness])
        square([bed_wall_thickness, shelf_thickness]);

    // 4. Inner flap (with flat-bottomed chamfer and top-right chamfer)
    difference() {
        polygon([
            [hook_total_width - wall_thickness, 0],
            [hook_total_width, 0],
            [hook_total_width, -hook_depth + wall_chamfer],
            [hook_total_width - wall_chamfer, -hook_depth],
            [hook_total_width - wall_thickness + wall_chamfer, -hook_depth],
            [hook_total_width - wall_thickness, -hook_depth + wall_chamfer]
        ]);
        // Subtract top-right corner chamfer (3mm)
        translate([hook_total_width, 0])
            polygon([
                [0, 0],
                [-3, 0],
                [0, -3]
            ]);
    }

    // 5. Support brace (gusset) - clean diagonal bar with chamfered inner corners
    theta = atan((brace_height - shelf_thickness) / brace_depth);
    
    // Chamfer points
    V1_shelf = [-brace_depth + brace_chamfer, -shelf_thickness];
    V1_diag  = [-brace_depth + brace_chamfer * cos(theta), -shelf_thickness - brace_chamfer * sin(theta)];
    V2_diag  = [-brace_chamfer * cos(theta), -brace_height + brace_chamfer * sin(theta)];
    V2_wall  = [0, -brace_height + brace_chamfer];
    
    V3 = [0, -brace_height - brace_thickness / cos(theta)];
    V4 = [-brace_depth - brace_thickness / sin(theta), -shelf_thickness];
    
    polygon([V1_shelf, V1_diag, V2_diag, V2_wall, V3, V4]);

    // 6. Corner chamfer inside the support brace triangle (shelf bottom to middle wall)
    polygon([
        [0, -shelf_thickness],
        [-brace_chamfer, -shelf_thickness],
        [0, -shelf_thickness - brace_chamfer]
    ]);

    // 7. Hook inner corner chamfers (optional)
    if (enable_hook_chamfer) {
        polygon([
            [wall_thickness, -shelf_thickness],
            [wall_thickness + brace_chamfer, -shelf_thickness],
            [wall_thickness, -shelf_thickness - brace_chamfer]
        ]);
        polygon([
            [hook_total_width - wall_thickness, -shelf_thickness],
            [hook_total_width - wall_thickness - brace_chamfer, -shelf_thickness],
            [hook_total_width - wall_thickness, -shelf_thickness - brace_chamfer]
        ]);
    }
}

module footprint2D() {
    union() {
        // Shelf footprint (chamfered or square)
        if (chamfer_shelf_corners) {
            polygon([
                [0, 0],
                [0, shelf_width],
                [-shelf_projection + corner_radius, shelf_width],
                [-shelf_projection, shelf_width - corner_radius],
                [-shelf_projection, corner_radius],
                [-shelf_projection + corner_radius, 0]
            ]);
        } else {
            translate([-shelf_projection, 0])
                square([shelf_projection, shelf_width]);
        }
        
        // Hook footprint (straight rectangle)
        translate([0, 0])
            square([hook_total_width, shelf_width]);
    }
}

module recess2D(offset_val = 0) {
    x_min = -shelf_projection + lip_width;
    x_max = hook_total_width; // Extended to the very back of the inner flap (no back lip)
    z_min = lip_width;
    z_max = shelf_width - lip_width;
    c_size = corner_radius - lip_width;

    // Shift coordinates by offset_val
    x_min_offset = x_min + offset_val;
    x_max_offset = x_max; // Open on bed side, no shift
    z_min_offset = z_min + offset_val;
    z_max_offset = z_max - offset_val;
    
    // Scale the chamfer size based on offset geometry:
    // c'_size = c_size - offset_val * (sqrt(2) - 1)
    c_size_offset = c_size - offset_val * (sqrt(2) - 1);

    if (chamfer_shelf_corners && c_size_offset > 0) {
        polygon([
            [x_max_offset, z_min_offset],
            [x_max_offset, z_max_offset],
            [x_min_offset + c_size_offset, z_max_offset],
            [x_min_offset, z_max_offset - c_size_offset],
            [x_min_offset, z_min_offset + c_size_offset],
            [x_min_offset + c_size_offset, z_min_offset]
        ]);
    } else {
        polygon([
            [x_max_offset, z_min_offset],
            [x_max_offset, z_max_offset],
            [x_min_offset, z_max_offset],
            [x_min_offset, z_min_offset]
        ]);
    }
}

module beveled_recess3D() {
    // We create a beveled 3D shape by taking the convex hull of two offset 2D slices at different heights.
    hull() {
        // Top slice (larger, slightly above Y=0 to avoid z-fighting)
        rotate([90, 0, 0])
            translate([0, 0, -0.1])
                linear_extrude(height = 0.1) {
                    recess2D(0);
                }
        // Bottom slice (smaller, offset inwards by recess_bevel_width at Y = -recess_depth)
        rotate([90, 0, 0])
            translate([0, 0, recess_depth])
                linear_extrude(height = 0.1) {
                    recess2D(recess_bevel_width);
                }
    }
}

module bed_shelf() {
    difference() {
        // Solid body: intersection of profile and footprint
        intersection() {
            // Extrude profile in Z
            linear_extrude(height = shelf_width) {
                profile2D();
            }
            // Extrude footprint in Y and rotate to align
            rotate([90, 0, 0])
                linear_extrude(height = 2 * hook_depth, center = true) {
                    footprint2D();
                }
        }
        
        // Recessed tray subtraction (with sloped inner walls)
        if (enable_tray) {
            beveled_recess3D();
        }
    }
}

// Center the shelf in XY and rotate it by 55 degrees around Z
// to maximize print area usage on the Bambu Lab A1 Mini (180x180x180mm).
// 55 degrees is the mathematically optimal angle for this asymmetric geometry,
// minimizing the bounding box and leaving equal clearance on both sides.
rotate([0, 0, 55])
    translate([-(-shelf_projection + hook_total_width)/2, hook_depth/2, 0])
        bed_shelf();

Download Model

Choose the model version and file format.

Model version