g | x | w | all
Bytes Lang Time Link
069Swift 6240907T231957ZmacOSist
048JavaScript V8240910T102513Zsech1p
035Fortran GFortran240910T060314Zroblogic
025Zsh +coreutils240909T072517Zroblogic
034Ruby240908T001719ZJordan
004Vyxal 3 j240907T184437Zpacman25
017Uiua231117T032325Zchunes
050Vim231215T214632ZAaroneou
nanPiet + asciipiet220701T084154ZAiden Ch
nanPiet + asciipiet220704T064811ZBubbler
019J230404T201020Zsouth
nan230118T200017ZThe Thon
056Wolfram Language Mathematica230116T113343ZJSorngar
042Arturo230116T084729Zchunes
054POSIX Shell Command Language + Utilities + V10 UNIX seq221122T194438Zнабиячлэ
029><>221110T142106ZEmigna
063Julia 1.0221031T193234ZAshlin H
052Julia 1.0221101T135230Zamelies
009Vyxal 2.14.1220629T075659Zlyxal
nanFig221029T214759Zsouth
030ARM Thumb2 machine code Linux221029T201319ZEasyasPi
104Pascal221029T182557ZKai Burg
092Python 3221001T180951ZJoao-3
009Japt R220913T162150ZShaggy
7515Nibbles220728T155712ZDominic
040PHP220701T212741ZIsmael M
031Knight220807T193042ZnaffetS
073Kotlin220727T225959ZEric Xue
036Befunge93220723T234446Zkevidryo
016Dyalog APL220712T081739ZVadim Tu
017K ngn/k220701T100747ZTKirishi
056Kotlin220709T161842Zwartoshi
021GolfScript220708T024127ZLeaky Nu
043PARI/GP220629T081318Zalephalp
166The Waterfall Model with output extension220703T050733Zm90
026MSDOS .COM format220630T012912ZErikF
053*><>220701T193313ZBee H.
067Acc!!220701T173224ZDLosc
2419x8616 machine code220701T144035Z640KB
6510Brainfuck + head + sh220701T093833Zmatteo_c
027x8632 machine code Linux executable220701T145207ZPeter Co
086brainfuck220629T111553ZElPedro
042Prelude220629T165259ZDingus
052Forth gforth220630T201418Zreffu
025Ly220630T183902Zcnamejj
010Stax220630T183241Zrecursiv
045Regenerate a s=220630T171352ZDLosc
063Java JDK220630T163055ZOlivier
015Pip220629T180631ZDLosc
065Haskell220630T135652Zmatteo_c
058Rust220629T114050Zlynn
094sed r220630T032455ZSisyphus
023stacked220630T003256ZConor O&
030Perl 5220630T000805ZXcali
044Ruby220629T215043ZAiden Ch
063JavaScript Node.js220629T215356ZMatthew
077vim220629T213035ZRay
102Bubblegum220629T212814ZnaffetS
010V vim220629T210622ZDJMcMayh
067GeoGebra220629T081432ZAiden Ch
033Raku220629T175913ZSean
010Jelly220629T163006ZJonathan
009Pyth220629T144605Zmath jun
008MATL220629T122618ZLuis Men
012BQN220629T090652ZDominic
036Factor220629T144008Zchunes
042Python 3220629T121530Zxnor
035PowerShell Core220629T142432Zuser3141
068Python 2220629T123308ZElPedro
100Whitespace220629T105337ZKevin Cr
048C clang220629T085808ZNoodle9
011Husk220629T104057ZDominic
019Retina220629T103704ZNeil
012Charcoal220629T103336ZNeil
063Clojure220629T094526Zovs
nanRust220629T083329Zmousetai
035R220629T085739ZDominic
018Burlesque220629T084634ZDeathInc
052Python 3220629T075423Ztsh
039jq nr220629T082430Zovs
010MathGolf220629T082211ZKevin Cr
01005AB1E220629T074213ZKevin Cr
012APL Dyalog Extended220629T074621ZAdá
038R220629T074247Zpajonk
066Python 2220629T073144Zpxeger
071Python 3220629T072555Zmousetai
026Zsh220629T072653Zpxeger

Swift 6, 84 69 bytes

(2...7).map{n in print(String((0...15).map{.init(.init(n*16+$0)!)}))}

JavaScript (V8), 48 bytes

for(i=32;i<127;i++)print(String.fromCharCode(i))

Try it online!

Fortran (GFortran), 35 bytes

print'(16A)',(char(j),j=32,127)
end

Try it online!

Zsh +coreutils, 25 bytes

jot -c 95 32 \~|rs -eg0 6

Try it online!

Beats the other Zsh solution by 1 byte!

Ruby, 34 bytes

puts [*" "..?~].join.scan /.{,16}/

Attempt This Online!

Vyxal 3 j, 4 bytes

kP₁Ẇ

Try it Online!

cool

Uiua, 18 17 bytes

≡&p⬚@ ↯∞_16+@ ⇡95

Try it!

≡&p⬚@ ↯∞_16+@ ⇡95
              ⇡95  # range from 0 to 94
           +@      # add space character
   ⬚@ ↯∞_16        # reshape into matrix 16 wide, filling excess with spaces
≡&p                # print each row

Vim, 50 bytes

i<C-r>=joi<Tab>m<Tab>r<Tab>32,126),'"<C-v><C-v>".v:val'),"")
<Esc>qq16|a
<Esc>q4@q

Try it online!

As pointed out by @Ray, if you have version 8.1.26 or newer, you'll have to replace r<Tab> with rang<Tab> for 53 bytes.

Explanation

i<C-r>=                                                       # Insert the result of:
               m<Tab>             ,                    )      #   Map...
                                   '"<C-v><C-v>".v:val'       #     Convert to char...
                     r<Tab>32,126)                            #     over range [32,126]
       joi<Tab>                                         ,"")  #   Join with empty strings
<Esc>
     qq                                                       # Record macro @q:
       16|                                                    #   Go to 16th character
          a                                                   #   Add a newline
<Esc>q
      4@q                                                     # Run macro @q 4 times

Piet + ascii-piet, 87 82 bytes (3×28=84 codels)

Thanks @Bubbler for -5 bytes by changing 4 dup * 2 * to a more space efficient 8 4 *

eeeumtqcsqrrjlvuddtrjcqdjes_eeeu ?????rr?jujrnvmsjiqqc _eeuu kdvtrbbbbb???vv   qq?

Try Piet online!

Might add a more detailed explanation later if I feel like to, but here's an explanation image generated by Bubbler's Piet interpreter for now. Can 100% be golfed, but I'm too lazy to do that right now lol.

Explanation image

The basic algorithm is to initially push 32 to the stack, then keep incrementing and outputting the character until 127, after which the code stops. At each iteration, it checks if the number+1 is a multiple of 16, and prints a newline if it is.

Longer explanation

Here's the ascii piet version of the code, with newlines added for clarity:

eeeumtqcsqrrjlvuddtrjcqdjes_
eeeu ?????rr?jujrnvmsjiqqc _
eeuu kdvtrbbbbb???vv   qq?

I'll be going over the code section by section.

Initialization:

eeeum
eeeu
eeuu

Push 8, Push 4, multiply. This puts 32 onto the stack (the codepoint for space)

Top half of the loop:

     tqcsqrrjlvuddtrjcqdje


What it does:

Commands                              Stack          DP,     CC
dup                                   [32,32]        right   left
out (char)                            [32]           right   left
1 + Dup                               [33,33]        right   left
4 dup * dup * 2 / 1 -                 [33,33,127]    right   left
- ! !                                 [33,1]         right   left
DP+                                   [33]           down    left

Basically, it adds 1 to the current codepoint and checks if it is equal to 127. If it is, then DP+ will turn the pointer 0 times and terminate the code. Otherwise, DP+ will turn the pointer 1 time and continue the loop.

Bottom right portion of the loop:


            ?jujrnvmsjiqqc
               ???vv   qq?

What it does:

Command                              Stack          DP,     CC
dup                                  [33,33]        down    left
4 dup *                              [33,33,16]     left    right
%                                    [33,1]         left    right
! 3 *                                [33,0]         left    right
1 CC+                                [33,0]         left    left
DP+                                  [33]           left    right

This checks if the current codepoint is a multiple of 16 (remember that we added 1 to the codepoint in the top half of the loop). If it is a multiple of 16, then DP+ will turn the DP to face downwards. Otherwise, the DP stays facing left. This is based on the observation that all the characters before a newline all have codepoints that are 1 less than a multiple of 16.

For example, let's say that at the beginning of this portion, the stack is [48]. This means that the ascii 47, or /, just got printed, and then that value was incremented by 1 from the top half of the loop. After taking this value modulo 16 and applied ! on the value, the stack becomes [48,1]. The 1 on the stack is then multiplied by 3 which causes DP+ to turn the DP 3 times, resulting in the DP facing down.

Bottom branch of the loop:

     t
     ?
     kdvtrbbbbb

What it does:

Command                              Stack          DP,     CC
1 5 dup +                            [33,1,10]      left    right
out (char) pop                       [33]           left    right

Basically just prints out a newline.

Top branch of the loop:

     t    rr
     ?????rr?

Nothing actually happens here. The code just passes through and enters back into the loop. One thing to note is that the reason why we did CC+ earlier was to get through the r block. Before the CC+, the CC was facing right, but that poses a problem when we go through the r block, because the code will continue execution through the top left of the block, which will break the entire code. Instead, we toggle the CC once to switch it to the left, which causes the code to exit out of the block from the bottom left.

Once the top half is entered again, dup out (char) is ran, printing out another character, and the loop continues.

Loop exit:

                          s_
                           _

Once the loop reaches 127, the loop is exited. s is essentially a noop that does nothing, and the program terminates in the _ block.

Piet + ascii-piet, 59 bytes (4×26=104 codels)

tliusqrfvrtqqqqijsmva???Bt  t?sd???vmjftrqaaaaqrsb_t eTt ee

Try Piet online!

How it works

4 dup * dup +         Initialize n to 32
Loop:
    dup outC          [n] Print as char
    1 + dup dup       [n n n] Add 1 and make two more copies
    4 dup * % !       [n n n%16==0] Is this number divisible by 16?
    CC+               [n n] If so,
        5 dup + outC        Print newline
    5 dup dup * * >   [n n>125] Is this number greater than 125?
    ! DP+             [n] If not, go back to start of Loop
outC                  Otherwise, print n (126) and exit

J, 19 bytes

echo _16]\u:32+i.95

Full program with trailing whitespace.

Attempt This Online!

echo _16]\u:32+i.95
               i.95  NB. range 0..94
            32+      NB. offset, 32..126
          u:         NB. convert codes to chars
     _16 \           NB. for every non-overlapping length 16 slice
        ]            NB. simply return that slice
echo                 NB. return the resulting table

Thunno N, \$5\log_{256}(96)\approx\$ 4.12 bytes

zC6AP

Attempt This Online!

Explanation

zC6AP   # Full program. No inputs.
zC      # Push the ASCII codepage as a string
  6AP   # Split into 6 chunks
        # The N flag automatically joins by newlines
        # And then it is output implicitly

Wolfram Language (Mathematica), 56 bytes

Print/@BlockMap[FromCharacterCode,32~Range~126,UpTo@16];

Unfortunately it doesn't work on TryItOnline due to an outdated interpreter that can't handle the UpTo abstraction in the BlockMap function call. I can offer this screenshot of my local machine: Screenshot "proof"

In case that ever gets fixed (or someone knows how to work around it), here is a TIO link anyway: Try it online!

If you are not in a notebook environment the ; at the end can be removed to save one byte.

Arturo, 42 bytes

loop split.every:16` `..`~`=>[print join&]

Try it

                   ` `..`~`                 ; a block of characters from space to tilde
     split.every:16                         ; split a block into groups of sixteen
loop                       =>[           ]  ; loop over every element in a block
                                        &   ; current element being looped over
                                    join    ; join block of chars to a string
                              print         ; print with newline

POSIX Shell Command Language + Utilities + V10 UNIX seq, 54 bytes

printf %b `seq -f'obase=8;"\";%g' 32 126|bc`|fold -w16

Self-explanatory: seq generates lines in the obase=8;"\";100 format, bc turns them into lines in the \144 format, printf expands them and pastes them together, fold wraps the output at 16 columns

There is, of course, trouble with seq. The minimal seq required here is Version 10 AT&T UNIX (and, hence, Plan 9), V8-V9 won't work since they take a -ppicture argument. Realistically this is not a problem since all modern systems include a V10-compatible seq, but.

Transcript of test cases:

$ cat b.sh; echo; wc -c b.sh
printf %b `seq -f'obase=8;"\";%g' 32 126|bc`|fold -w16
54 b.sh
$ ./b.sh; echo
 !"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~
$ ./b.sh | hd
00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f  | !"#$%&'()*+,-./|
00000010  0a 30 31 32 33 34 35 36  37 38 39 3a 3b 3c 3d 3e  |.0123456789:;<=>|
00000020  3f 0a 40 41 42 43 44 45  46 47 48 49 4a 4b 4c 4d  |?.@ABCDEFGHIJKLM|
00000030  4e 4f 0a 50 51 52 53 54  55 56 57 58 59 5a 5b 5c  |NO.PQRSTUVWXYZ[\|
00000040  5d 5e 5f 0a 60 61 62 63  64 65 66 67 68 69 6a 6b  |]^_.`abcdefghijk|
00000050  6c 6d 6e 6f 0a 70 71 72  73 74 75 76 77 78 79 7a  |lmno.pqrstuvwxyz|
00000060  7b 7c 7d 7e                                       |{|}~|
00000064

Here's a 100-byte fully-standards-conformant version, same output:

i=32
printf %b $(while [ $i -le 126 ]
do
printf %s\\n 'obase=8;"\";'$i
i=$((i+1))
done|bc)|fold -w16

><>, 30 29 bytes

84*v
%?!\:e9*)?;:o1+:82*
ao \

Try it online

Julia 1.0, 69 63 bytes

map(i->println(String(reshape([' ':'~';' '],(16,6))[:,i])),1:6)

Try it online!

-6 bytes thanks to @amelies: ' ':'~' is a valid range in Julia; no need to convert from integers

Julia 1.0, 53 52 bytes

0:16:80 .|>n->(prod(' ':'~')*" ")[n+1:n+16]|>println

Try it online!

Vyxal 2.14.1, 9 bytes

kPð+s16ẇ⁋

Try it Online!

So turns out kP isn't actually all of printable ascii - it's missing the space character, and it isn't in the right order either, meaning that for the purposes of this challenge, it shouldn't be banned, as it doesn't print "the ascii table" and it isn't exactly "trivial" either.

(And if it isn't allowed, then that's grounds enough for VTCing this challenge as needs details or clarity, because "trivialising the challenge" isn't objectively defined - there's an argument to be made that kPð+s isn't a trivial built-in).

Explained

kPð+s16ẇ⁋
kPð+       # a string of 0-9a-Z + python's string.punctuation + space
    s      # sorted to be in printable ascii order
     16ẇ   # split into parts of length 16
        ⁋  # joined on newlines

Vyxal, 5 bytes

kP6/⁋

Try it Online!

Bug fixes make this 5 bytes in reality.

Fig, \$17\log_{256}(96)\approx\$ 13.993 bytes

Without printable ascii builtin

nOC+32r95 16'+xcn

Try it online!

With printable ascii builtin \$10\log_{256}(96)\approx\$ 8.231 bytes

ncp16'+xcn

Try it online!

nOC+32r95 16'+xcn
      r95          # range [0,95)
   +32             # add 32 to each, range [32,126)
  C                # convert each element to str
 O                 # join by nothing
n         16       # every 16th element, apply the function
            '+     # add
               cn  # newline
              x    # to the element

ncp16'+xcn
 cp                # printable ascii
n  16'+xcn         # same as above

ARM Thumb-2 machine code (Linux), 30 bytes

20 24 06 a1 01 22 01 34 25 07 08 bf 02 22 04 27
01 20 00 df 0c 70 7f 2c f3 d1 00 de 20 0a

Put this in a read-write-execute section at a 4 byte aligned address.

Assembler source:

    .syntax unified
    .arch armv7-a
    // Store in a read-write-execute section so I don't need to address
    // a different section or use the stack for my buffer
    .section ".wtext", "awx", %progbits
    .thumb
    .globl _start
    .thumb_func
    // Aligned address is necessary for the buffer
    .p2align 2, 0
_start:
    // First char
    movs    r4, #' '
.Lloop:
    // syscall setup for write(1, .Lbuf, 1)
    movs    r0, #1
    adr     r1, .Lbuf
    movs    r2, #1
    movs    r7, #4
    // Increment r4
    adds    r4, #1
    // And test r4 & 0xF to see if the NEXT char is a multiple
    // of 16. I do this with my favorite narrow instruction, LSLS,
    // where I shift out all the other bits into a dummy register and set
    // the flags.
    lsls    r5, r4, #32 - 4
    // If so, set the length to 2 to print the newline
    // if ((r4 & 0xF) == 0) length = 2;
    it      eq
    moveq   r2, #2
    // Syscall
    svc     #0
    // Store new byte into the buffer. This is why a writable .text
    // is needed.
    strb    r4, [r1]
    // Check if we are at the end (DEL), if not, loop again
    cmp     r4, #0x7f
    bne     .Lloop
.Lexit:
    // Exit with a SIGILL crash because I don't care :)
    udf     #0
    .p2align 2, 0
    // mutable buffer
.Lbuf:
    // Current char
    .byte ' '
    // Additional newline
    .byte '\n'

enter image description here

Pascal, 104 B

This source code complies with the ISO standard 7185 “Standard Pascal” (level 0).

program p(output);var c:char;begin for c:=' 'to'~'do if ord(c)mod 16=15 then writeLn(c)else write(c)end.

The output lacks of trailing newline.

Extended Pascal, 94 B

In “Standard Pascal” (ISO standard 7185) width specifiers in have to be positive. That means the n in write(123:n) has to be ≥ 1. “Extended Pascal”, as laid out in ISO standard 10206, permits a zero-write-width format specifier. We can utilize this and “conditionally” print chr(10) – the newline character as used in Unixoid environments (Linux, FreeBSD, …).

program p(output);var c:char;begin for c:=' 'to'~'do write(c,chr(10):ord(ord(c)mod 16=15))end.

In Pascal, the newline character is the only character that may not appear in a string literal. Thus chr(10) cannot be shortened any further.

Python 3, 92 bytes

print('\n'.join([''.join([chr(32+j+16*i)for j in range(16)])for i in range(6)]).rstrip(''))

Note that the blank looking character inside rstrip is DEL.

Try it online!

Explanation:

                [''.join([chr(32+j+16*i)for j in range(16)])for i in range(6)] # generate rows of string
      '\n'.join(                                                              ) # join with new lines
                                                                               .rstrip('') # strip the DEL out
print(                                                                                    
 ) # then print

Japt -R, 9 bytes

#_odH ¬òG

Test it

Nibbles, 7.5 bytes (15 nibbles)

`/16+' '`,95
`/16+' '`,95
        `,95    # range from 0 to 95
    +' '        # add each to a space character
`/16            # and split into chunks of 16

enter image description here


Nibbles, 5 bytes (10 nibbles)

`/16>>`<@

Uses the special value assigned to @ when input is empty, corresponding to a list of ascii characters including newline, in a non-standard order.


`/16>>`<@
        @       # " abcdefghijklmnopqrstuvwxyz.,!?_"
                # + newline +
                # "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-+:;\"'~`@#$%^&*()[]{}<>\\/=|"
      `<        # sort 
    >>          # remove first item (newline)
`/16            # and split into chunks of 16
```

PHP, 43 40 bytes

<?=chunk_split(join(range(' ','~')),16);

Simply outputs the expected text, without any trailing newlines.

Just run php file.php and it should produce the output.

Try it on: https://onlinephp.io/c/013ce


Thanks to Steffan for -3 bytes.

Knight, 31 bytes

;=a 32W>127aO+AaI%=a+1a 16"\"""

Try it online!

Unminified:

; = a 32
: WHILE (> 127 a)
    : OUTPUT (+ (ASCII a) (IF (% (= a (+ 1 a)) 16) "\" ""))

Kotlin, 89 73 bytes

print((' '..'~').chunked(16).map{it.joinToString("")}.joinToString("\n"))

Kotlin Playground

Edits

Befunge-93, 36 bytes

" ">:,1+:"~"1+\`v
   ^            _@

Try it online!

I don't know if i can optimize this code even further than i did.

An explanation

This code is comprised of 5 main parts:

Dyalog APL, 16 bytes

⎕UCS 6 16⍴31+⍳95

K (ngn/k), 21 17 bytes

-4bytes thanks to Traws!

`0:`c$6 16#32+!95

It's my first answer in K! So there might be some optimizations ^^

`0:`c$6 16#32+!95
           32+!95 / Creates an array from 32 to 126
      6 16#       / Reshapes it to 16 columns and 6 lines (16 can me replaced by 0N)
   `c$            / For each elements of this array/matrix, convert the int to a char
`0:               / Print the result whitout ( and "

Try it online!

Kotlin, 80 72 56 bytes

56 bytes versuin

(' '..'~').chunked(16).flatMap{it+"\n"}.joinToString("")

Directly using a char range instead of an integer range.

Try it online!

72 bytes version

(32..126).chunked(16).flatMap{it.map{it.toChar()}+"\n"}.joinToString("")

Try it online!

80 bytes version

(32..126).chunked(16){it.joinToString(""){""+it.toChar()}+"\n"}.joinToString("")

Try it online!

GolfScript, 21 bytes

96,{[32+]}[/]''*16/n*

Try it online!

PARI/GP, 43 bytes

for(i=1,95,printf("%c",i+31);i%16||print())

Attempt This Online!


PARI/GP, 44 bytes

for(i=2,7,print(Strchr([s=16*i..s+15-i\7])))

Attempt This Online!

The Waterfall Model (with output extension), 166 bytes

[[30000,7,7,7,7,7,7,7],[3,2,2,0,0,0,7,0],[2,2,2,0,0,0,3,0],[1,0,2,300,0,0,0,0],[66,2,0,0,302,0,9,0],[4822,2,0,0,300,5100,9,0],[9,0,0,0,0,0,1,0],[29998,0,0,0,0,0,0,0]]

Try it online!

$$ \begin{array}{c|c|ccccccc} &start&A&B&α&β&γ&out&halt\\\hline A&3&2&2&&&&7&\\ B&2&2&2&&&&3&\\ α&1&&2&300&&&&\\ β&66&2&&&302&&9&\\ γ&4822&&&&300&5100&9&\\ out&9&&&&&&1&\\ halt&29998&&&&&&& \end{array} $$

MS-DOS (.COM format), 33 30 26 bytes

Iterates through 20H and 7EH, printing each character and a line break between the 16th character of each line.

0000 B0 20 CD 29 40 3C 7F 74 10 A8 0F 75 F5 50 B0 0A
0010 CD 29 B0 0D CD 29 58 EB E9 C3

Assembly version (TASM):

IDEAL
P8086

MODEL   TINY
CODESEG
ORG 100H

MAIN:   
    MOV  AL,20H ; Start at ' '
PRINT:
    INT  29H    ; Print character
    INC  AX     ; Increment character
    CMP  AL,7FH ; Done?
    JZ   DONE   ; Yes, exit
    TEST AL,0FH ; End of line?
    JNZ  PRINT  ; No, next character
    PUSH AX     ; Save current character
    MOV  AL,0AH ; Print CR+LF
    INT  29H
    MOV  AL,0DH
    INT  29H
    POP  AX     ; Restore current character
    JMP  PRINT  ; Next character
DONE:
    RET

END MAIN
ENDS

*><>, 53 bytes

e9*&1f+2*   v
v;?=&:&:+1o:<
\:1f+%?!`   /
        `ao

Try it online!

Unfortunately, I feel like a lot of these bytes are spent setting, storing, and checking if we're at 126 yet, but I'm unsure of a better way of accomplishing this.

e9*&            push 126 to the stack, and pop it into a register
    1f+2*       push 32 to the stack
            v   Change IP direction downwards

            <   Change IP direction to the left
          o:    Duplicate the top of the stack, then pop it to STDOUT as a char
       :+1      Add one to the value on the top of the stack, and duplicate it
    &:&         Pop the register back onto the stack, duplicate it, then pop that into the register
 ;?=            If our two duplicated values match, end execution
v               Change IP direction downwards

\               Mirror IP to the right
 :              Duplicate the top of the stack
  1f+           Put 16 on the stack
     %          Get the modulus of our counter and 16
      ?!`       If the result is 0, drop down a line, and 
         ao         Print a newline
        `           And raise back up a line
            /   Else, mirror the IP up

Acc!!, 67 bytes

Count i while i-100 {
1/((i+1)%17+1)
Write 10*_+(i+32-i/17)*(1-_)
}

Try it online!

Explanation

We run a single loop that outputs one character each time, using math to switch between ASCII characters and newlines.

The loop index i runs from 0 through 99. We want to print newlines at indices 16, 33, 50, 67, and 84--that is, where (i+1)%17 is zero. With no comparison operators in Acc!!, we use integer division instead: when (i+1)%17 is zero, 1/((i+1)%17+1) is one, and it is zero otherwise. We store this quantity in the accumulator.

Now if the accumulator value is one, we want to write a newline, and if it is zero, we want to write something else. This is easily accomplished by multiplying the two quantities by _ and (1-_) respectively, and adding the results: Write 10*_+(...)*(1-_). It only remains to calculate the ASCII character's codepoint. At first, it is just i+32, but we need to adjust for the newlines. The number of newlines printed so far is i/17, so subtracting this from the index gives us the correct codepoint formula: i+32-i/17.


A more straightforward solution with two loops is 73 bytes:

Count i while i-6 {
Count j while j-16+i/5 {
Write i*16+32+j
}
Write 10
}

x86-16 machine code, PC DOS, 24 19 bytes

00000000: b020 bb0a 0daa 40a8 0f75 0393 ab93 3c7e  . ....@..u....<~
00000010: 7ef3 c3                                  ~..

Listing:

B0 20           MOV   AL, 20H       ; starting codepoint
BB 0D0A         MOV   BX, 0D0AH     ; CR/LF chars
            CP_LOOP: 
AA              STOSB               ; write AL to buffer
40              INC   AX            ; increment to next char
A8 0F           TEST  AL, 0FH       ; is end of line?
75 03           JNZ   CP_NEXT       ; jump if so
93              XCHG  AX, BX        ; save current codepoint
AB              STOSW               ; write CR/LF to buffer
93              XCHG  AX, BX        ; restore codepoint
            CP_NEXT: 
3C 7E           CMP   AL, 07EH      ; <= 7EH? 
7E F3           JLE   CP_LOOP       ; if so, keep looping 
C3              RET                 ; return to caller

enter image description here

Using @PeterCordes's suggestion to write to an output buffer instead of direct to console.

Callable function, output to string buffer ES:[DI].

Brainfuck + head + sh, 65 + 10 bytes

>-[-[-<]>>+<]>->++++++[->>++++[-<++++>]<[-<<.+>>]++++++++++.[-]<]

Execute <brainfuck-program>|head -c-2 (see meta).

head removes a trailing \del char of the brainfuck program output (as noted by @mousetail).

Attempt This Online!

x86-32 machine code (Linux executable) 27 bytes

cut-down NASM listing: Address | machine code | source

                   global _start
                   _start:            ; Linux processes (and thus static executables) start with registers zeroed, except ESP
                   asciitable:
 00 B020            mov al, ' '
 02 B20A            mov dl, 0xa
 04 89E7            mov edi, esp      ; space above ESP on the stack is argc, argv[] and env[] array elements, and the env strings.  We overwrite that.
                   .loop:
 06 AA              stosb
 07 40              inc  eax            ; there is no branch condition for AF, the half-carry flag :/
 08 A88F            test al, 0x8f       ; detect mod16 and when we've gone past printable
 0A 7503            jnz .nonewline
 0C 92              xchg  eax, edx
 0D AA              stosb
 0E 92              xchg  eax, edx
                   .nonewline:
                    ; FLAGS still set from earlier TEST
 0F 79F5            jns  .loop               ; stop at 0x80, beyond what the syscall uses
                     ; mov [edi-1], 0xa      ; no trailing newline
                   
 11 B004            mov  al, 4   ; __NR_write
                    ; ebx=0 from process startup; writing to stdin happens to work on a terminal
 13 89E1            mov  ecx, esp
 15 8D5060          lea  edx, [eax-4 + 0x7f-0x20 + 5]  ; exclusive range, 5 newlines; final line does not end with newline as whitespace isn't required
 18 CD80            int  0x80    ; write(0, ecx, 100)
                   
                   ; mov  eax,1      ; exit(ebx)
                   ; int  0x80
 1A CC              int3  ; abort program

Fill bytes into a buffer (overwriting stack memory above ESP, starting with argc, argv[], and into env[]). We go a bit beyond 0x7e because that makes the stop condition cheaper, but those bytes aren't printed: we have to generate an explicit length in EDX anyway, so we just omit it.

We make one write(0, buf, 100) system call. 0 is STDIN_FD, but on a normal xterm/konsole/gnome_terminal, FD 0,1, and 2 are all read+write duplicates of the same file description. So we save 1 byte for inc ebx. If you want to pipe the output into something to hexdump for example, ./asciitable 0>&1 | hexdump -C

Instead of existing cleanly, we use int3 to raise a debug exception, resulting in the OS delivering SIGTRAP, killing the process. A typical shell will then print Trace/breakpoint trap (core dumped), but that's the shell, not this program. This saves 3 bytes vs. mov al, 1 / int 0x80 (the upper bytes of EAX are zeroed, unless write returned an error).

I considered letting execution just fall off the end (to probably 00 00 padding added by the linker, which decodes as add [eax], al and will segfault), but we're already stretching things a bit by only counting the .text section of the executable, not the whole file size. (Unlike a DOS .com, there is metadata)

Our output doesn't end with a newline; as the question says, no trailing whitespace is required after the 0x7e ~ character.

The mod16 detection via checking the low bits 4 of AL is fairly straightforward, but was borrowed from ErikF's the x86-16 MS-DOS answer which I read first before thinking about how I'd do it.

But to enable a jns as the loop branch without a separate cmp, we also set the MSB in our mask for test. That's not a spot we wanted a newline anyway. I'd hoped to be able to branch on FLAGS from inc al (2 bytes), but since we'd also need something for newlines, this is even better: we can now use inc eax (1 byte) and still branch on the MSB of AL. Using mov dl, 0xa (2B) / xchg/stosb/xchg (1B each) instead of mov byte [edi], 0xa (3 bytes) / inc edi (1 byte but clobbers FLAGS) was the key to that, saving another test or cmp for a net saving of 1 byte.

Demo:

$ nasm -felf32 asciitable.asm
$ ld -melf_i386 -o asciitable asciitable.o
$ ./asciitable
 !"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~Trace/breakpoint trap (core dumped)

$ ./asciitable 0>&1 | cat  # suppresses signal diagnostic from shell
 !"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~peter@volta:/tmp$

$ ./asciitable 0>&1 | hexdump -C
00000000  20 21 22 23 24 25 26 27  28 29 2a 2b 2c 2d 2e 2f  | !"#$%&'()*+,-./|
00000010  0a 30 31 32 33 34 35 36  37 38 39 3a 3b 3c 3d 3e  |.0123456789:;<=>|
00000020  3f 0a 40 41 42 43 44 45  46 47 48 49 4a 4b 4c 4d  |?.@ABCDEFGHIJKLM|
00000030  4e 4f 0a 50 51 52 53 54  55 56 57 58 59 5a 5b 5c  |NO.PQRSTUVWXYZ[\|
00000040  5d 5e 5f 0a 60 61 62 63  64 65 66 67 68 69 6a 6b  |]^_.`abcdefghijk|
00000050  6c 6d 6e 6f 0a 70 71 72  73 74 75 76 77 78 79 7a  |lmno.pqrstuvwxyz|
00000060  7b 7c 7d 7e                                       |{|}~|
00000064

$ strace ./asciitable 0>/dev/null
execve("./asciitable", ["./asciitable"], 0x7ffc63d167c0 /* 55 vars */) = 0
[ Process PID=2670503 runs in 32 bit mode. ]
strace: WARNING: Proper structure decoding for this personality is not supported, please consider building strace with mpers support enabled.
write(0, " !\"#$%&'()*+,-./\n0123456789:;<=>"..., 100) = 100
--- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---
+++ killed by SIGTRAP (core dumped) +++
Trace/breakpoint trap (core dumped)

Try it online! with an inc ebx added so it writes to stdout and thus shows up in the tio.run output pane. IDK why it didn't show in the debug pane. (32-bit code supported by having FASM make an executable directly, instead of NASM.)

TODO: a function instead of program, filling a buffer

We'd obviously save the syscall code, but could no longer write past the end of the actual stop point. So would need maybe 1 more byte in the loop to go back to cmp al, 0x7e / jne as the loop condition.

brainfuck, 64 86 bytes

+++++[>++>++++++>+<<<-]>>++>+[-[>+<[->>+<<]]>>[-<<+>>]<+++++++++++++++[<<.+>>-]<<<.>>]

Try it online!

No longer prints 0x7f as the last character.

With many thanks to @Jiří for the fix.

Prelude, 50 42 bytes

88+
6(1-) v^^(  1-++!)^6-!)
1- v+(1-) v(1-

Try it online!

Explanation

88+
6(1-)
1- v+

Push \$16\$ to Voice 1 and \$95\$ to Voice 3.


       v^^
      (1-)

For each value from \$95\$ down to \$1\$, push that value and two copies of \$16\$ to Voice 2. When the loop ends, the stack in Voice 2 is [..., 0, 0, 0, 95, 16, 16, 94, 16, 16, ..., 1, 16, 16].


         (  1-++!     )

For each triplet of values in Voice 2, sum them and subtract \$1\$, then print the result as an ASCII character...


                 )
          v(1-

in groups of 16...


                  ^6-!

with a newline appended to each group.

Printing fails on the (non-existent) 96th character because the triplet of values summed is [0, 0, 0], with the result that the code attempts to convert \$-1\$ to ASCII and thus exits with an out-of-range error.


Fugue

Fugascii

Fugue is a musical encoding for Prelude. Fugascii*, a short trio for woodwinds, was generated from the Prelude code above using a script that I wrote for this purpose. Read all about it in this answer.

* The title is a pun on a previous Fugue composition of mine, Fugacity.

Forth (gforth), 52 bytes

: f 127 32 do i emit i 16 mod 15 = if cr then loop ;

Try it online!

Explanation

Loops from 32 to 127 (end is non-inclusive). Outputs the ascii char for each number, if index % 16 == 15, outputs a newline.

Code Explanation

: f            \ start a new word definition
  127 32       \ set up loop bounds
  do           \ start a new counted loop
    i emit     \ output the ascii character for the current loop index
    i 16 mod   \ get index modulo 16
    15 =       \ check if result is equal to 15
    if cr then \ if it is, output a newline
  loop         \ end the counted loop
 ;             \ end the word definition

Ly, 25 bytes

88+sp' '~R' r[l[fo,]p9`o]

Try it online!

This one is pretty straightforward. It uses a range command to generate the codepoints, the two loops to print them with a \n every 16 characters.

88+sp                     - stash the number 16
     ' '~R                - push codepoints from " " to "~" to the stack
          ' r             - push a blank (for DEL) and reverse stack
             [          ] - loop until the stack it empty
              l           - push 16 (from back up cell)
               [  ,]p     - loop 16 times...
                fo        - pull codepoint forward and print as char
                     9`o  - print a LF

Stax, 10 bytes

Ç·qª>%bΓ½⌠

Run and debug it

Approach

Regenerate -a -s=, 45 bytes

[ -/]|
|[0-?]|
|[@-O]|
|[P-_]|
|[`-o]|
|[p-~]

Attempt This Online!

Explanation

The -a flag requests all possible matches; the -s= flag sets the separator between matches to empty string. Then the regex is just a big alternation: "match any character from to /, or a newline, or any character from 0 to ?, or a newline, or..." The regex always matches one character, and the -a flag outputs all of them.

Java (JDK), 63 bytes

v->{for(char c=31;++c<127;)System.out.print(c%16>14?c+"\n":c);}

Try it online!

Pip, 15 bytes

P*C:32,127<>16u

Try It Online!

Explanation

P*C:32,127<>16u
    32,127       Range(32, 127)
          <>16   Group into chunks of size 16 (with the last one being shorter)
  C:             Convert to characters
P*               Print each sublist (concatenated together, by default)
              u  Suppress autoprinting of the last expression

Or, with the -l flag to handle the output formatting, it would be 12 bytes:

C:32,127<>16

Using built-in

PA is a string containing all printable ASCII characters, so:

PA<>16Jn

Or, with the -l flag:

PA<>16

Haskell, 65 bytes

_#[]=[]
n#x=take n x:n#drop n x
main=putStr$unlines$16#[' '..'~']

Attempt This Online!

Rust, 58 bytes

||for i in' '..''{print!("{:
<1$}",i,i as usize%16/15+1)}

Attempt This Online!

Explanation

sed -r, 94 bytes

s/.*/ABCDEFGHIJKLMNO\nPQRSTUVWXYZ/
saa !"#$%\&'()*+,-./\n0123456789:;<=>?\n@&[\\]^_\n`\L&{|}~a

Try it online!

This is probably the best that can be done in sed, which has no functionality for ranges of characters. The main byte saving is in the fact we can generate ABCDEFGHIJKLMNOPQRSTUVWXYZ and then use \L to get the lowercase version.

stacked, 23 bytes

95:>32+16 chunk chr out

Try it online!

Slightly shorter than the more obvious 24 bytes:

32 126|>16 chunk chr out

Explanation

95:>32+16 chunk chr out
95:>                       the range of numbers from 0 to 94 inclusive
    32+                    add 32 to each
       16 chunk            split into chunks of 16
                chr        convert each number to a character
                    out    output it

stacked's default output behavior with out for 2D character arrays is exactly joining them, and produces the correct output.

Perl 5, 30 bytes

say map$/x!($_%16).chr,32..126

Try it online!

Ruby, 53 44 bytes

Thanks user for telling me about each_slice, saving 9 bytes!

(' '..'~').each_slice(16).each{|x|puts x*""}

Try it online!

My first time golfing in Ruby lol, just wanted to try a new language :D . Tell me if there are any golfs, I'm anticipating a lot!

JavaScript (Node.js), 63 bytes

for(i=33,a=' ';i<129;a+=Buffer([i++]))i%16||console.log(a,a='')

Attempt This Online!

vim, 77 bytes

qqA<C-V><C-A><C-V><ESC><ESC>v0da0x0<ESC>@"0xII<C-V><C-V><ESC>A<C-V><ESC><ESC>v0d@"qxV!seq 32 126
:%norm @q
VgggJ0qq16li
<ESC>q4@q

<C-V> is 0x16. <C-A> is 0x01. <ESC> is 0x1b.

Annotated

qq                                   # define macro q that changes e.g. "48" to "0" as follows:
    A<C-V><C-A><C-V><ESC><ESC>          # Change e.g. "48" to "48<INC>"
    v0d                                 # Place the above command in register "
    a0x0<ESC>@"                         # Write 0x0, then use above command to increment. Thus, "48" becomes "0x30"
    0xII<C-V><C-V><ESC>A<C-V><ESC><ESC> # Convert "0x30" to "I<C-V>x30<ESC>" (command that appends character 0x30, i.e. "0")
    v0d@"                               # Pull command into register and execute
q
xV!seq 32 126         # Populate file with numbers 32-126, one per line.
:%norm @q             # Execute macro q over each line
VgggJ                 # Join lines
0qq16li<NL><ESC>q     # Insert a newline 16 characters from the start, and record as a macro while we're at it
4@q                   # Execute that macro 4 more times

Try it online!

Bubblegum, 102 bytes

00000000: 5350 5452 5651 5553 d7d0 d4d2 d6d1 d5d3  SPTRVQUS........
00000010: e732 3034 3236 3135 33b7 b0b4 b2b6 b1b5  .20426153.......
00000020: b3e7 7270 7472 7671 7573 f7f0 f4f2 f6f1  ..rptrvqus......
00000030: f5f3 e70a 080c 0a0e 090d 0b8f 888c 8a8e  ................
00000040: 898d 8be7 4a48 4c4a 4e49 4d4b cfc8 ccca  ....JHLJNIMK....
00000050: cec9 cdcb e72a 282c 2a2e 292d 2baf a8ac  .....*(,*.)-+...
00000060: aaae a9ad 0300                           ......

Try it online!

For some reason, compressing it made it actually a bit larger than the original string.

V (vim), 10 bytes

¬ ~5ñ16|á

Try it online!

Explanation:

¬               # Insert characters between the code points...
  ~             #   "space" and "~"
   5ñ           # 5 Times...
     16|        #   Go to the 16th character on this line
        á<cr>   #   Append a newline

Hexdump:

00000000: ac20 7e35 f131 367c e10d                 . ~5.16|..

GeoGebra, 68 67 bytes

UnicodeToText(Flatten(Zip(If(Mod(a,16)>14,{a,10},{a}),a,32...126)))

Text is shown on the graph.

Might be golfable with some different strategy, but I'm not too experienced with GeoGebra.

Try It Online!

Raku, 33 bytes

.say for (' '..'~').join.comb(16)

Try it online!

Jelly, 10 bytes

32r126Ọs⁴Y

Try it online!

5 if we can just use our langauage's features:

ØṖs⁴Y

How?

32r126Ọs⁴Y - Link: no arguments
32         - 32                }
   126     - 126               }
  r        - inclusive range   }
      Ọ    - cast to ordinals  } - together this is the same as ØṖ yields
        ⁴  - 16
       s   - split into chunks of length (up to 16)
         Y - join with newline characters

Pyth, 15 9 bytes

jsMc6rd\

Try it online!

There's a literal DEL character after the \.

jsMc6rd\
     rd\  Half-inclusive string range from ' ' (space) to DEL
   c6     Chop into 6 equal-sized lists (last chunk is smaller)
 sM       Join each list on '' (empty)
j         Join on newlines, implicitly output

MATL, 8 bytes

'~':16e!

Try it online! Outputs a trailing whitespace (to make the last row have the same length as the others).

How it works

'~'   % Push this character
:     % Range from space to that character, including both. Gives a character vector
16e   % Format as a 16-row character matrix, in major-column order. Pads with
      % character 0 at the end
!     % Transpose
      % Implicit display. Character 0 is displayed as space

BQN, 12 bytes

6‿16⥊' '+↕95

Try it at BQN REPL

Output has a trailing space character.

BQN's affine character space is quite neat here: adding a number to a character gives another character. So: create a range from zero to 95 (↕95), add it to the space character (' '+), and reshape to 6-row, 16-column format (6‿16⥊).


BQN, 26 bytes

@+100⥊⍉(32+⍉6‿16⥊↕95)∾6⥊10

Try it at BQN REPL

Longer version with output string that is truly "exactly like shown above", written before the edit allowing trailing whitespace.
Creates a transposed array of character values (32+⍉6‿16⥊↕95), appends a newline value 10 to each column (∾6⥊10), re-transposes () & extracts the first 100 values (100⥊), and finally converts to characters (@+).

Factor, 36 bytes

32 126 [a,b] 16 group "\n"join write

Attempt This Online!

Python 3, 42 bytes

print(('%c'*16+'\n')*6%(*range(32,127),9))

Try it online!

Includes a trailing tab on the last line and a trailing newline.

PowerShell Core, 35 bytes

-join(' '..'~')-split'(.{16})'-ne''

Try it online!

     (' '..'~')                     # create a list of characters from <space> to ~
-join                               # join the list to a single string
               -split'(.{16})'      # split the string every 16 chars ...
                              -ne'' # ... and let only non-empty strings pass.

Python 2, 68 bytes

l=''.join(chr(x+32)for x in range(95))
while l:print l[:16];l=l[16:]

Try it online!

Whitespace, 110 100 bytes

[S S S T    T   T   T   T   N
_Push_31][N
S S N
_Create_Label_LOOP][S S S T N
_Push_1][T  S S S _Add][S N
S _Duplicate][S S S T   T   T   T   T   T   T   N
_Push_127][T    S S T   _Subtract][N
T   S T N
_If_0_Jump_to_undefined_Label][S N
S _Duplicate][T N
S S _Print_as_character][S N
S _Duplicate][S S S T   S S S S N
_Push_16][T S T T   _Modulo][S S S T    T   T   T   N
_Push_15][T S S T   _Subtract][N
T   T   N
_If_neg_Jump_to_Label_LOOP][S S S T S T S N
_Push_10_\n][T  N
S S _Print_as_character][N
S N
N
_Jump_to_Label_LOOP]

Letters S (space), T (tab), and N (new-line) added as highlighting only.
[..._some_action] added as explanation only.

Try it online (with raw spaces, tabs and new-lines only).

Explanation in pseudo-code:

Integer n = 31
Start LOOP:
  n = n + 1
  If (n==127):
    Stop program with error by jumping to undefined Label
  Print n as character to STDOUT
  If (n modulo 16 < 15):
    Go to next iteration of LOOP
  Print '\n'
  Go to next iteration of LOOP

C (clang), 51 \$\cdots\$ 49 48 bytes

f(c){for(c=32;putchar(c)<126;++c%16||puts(""));}

Try it online!

Saved a 2 bytes thanks to tsh!!!
Saved a bytes thanks to ovs!!!

Husk, 11 bytes

↓2C16mcŀ127

Try it online!

       ŀ127 # range from zero to 126
     mc     # map across this & get characters
  C16       # cut into sublists with length 16
↓2          # drop the first 2 of them

Retina, 19 bytes


6*$(16*1¶
Y`1` -~_

Try it online! Explanation:


6*$(16*1¶

Insert 6 rows of 16 1s.

Y`1` -~_

Cyclically transliterate the first 95 1s to the range from to ~ and delete the 96th.

Charcoal, 12 bytes

UT⪪⪫…· ¦~ω¹⁶

Try it online! Link is to verbose version of code. Explanation:

UT

Disable automatic padding (would normally output a trailing space on the last line).

⪪⪫…· ¦~ω¹⁶

List the characters from to ~ inclusive, join them together, split into groups of 16, and implicitly print each group on its own line.

Save 6 bytes by using the builtin γ in place of ⪫…· ¦~ω. Try it online! Link is to verbose version of code.

Clojure, 72 63 bytes

(doseq[x(range 32 127)](print(str(char x)({15 \
}(rem x 16)))))

Try it online!

Rust, 74 72 70 64 bytes

(' '..'').any(|i|print!("{i}")>if i as u8%16>14{print!("
")});

Text contains non-printable characters so might not render correctly. @Antiip saved me some bytes.

Attempt This Online!

R, 35 bytes

write(intToUtf8(32:126,T),1,16,,"")

Try it online!

Burlesque, 18 bytes

@!~r@' +]16co)++un

Try it online!

@!~  # Push ! and ~ to stack
r@   # Range between them
' +] # Prepend " "
16co # Chunks of 16
)++  # Concat
un   # Intercalate newlines between blocks and join

Python 3, 52 bytes

for i in range(95):print(chr(i+32),end='\n'[~i%16:])

Try it online!

jq -nr, 39 bytes

[range(32;127)]|implode|scan(".{1,16}")

Attempt This Online!

The best I can do without regex is 40 bytes:

16*range(2;8)|[.+range(16-./99)]|implode

Attempt This Online!

MathGolf, 10 bytes

♣⌡╒T≥$y☻/n

Try it online.

Explanation:

♣           # Push 128
 ⌡          # Decrement it by 2 to 126
  ╒         # Pop and push a list in the range [1,126]
   T        # Push 31
    ≥       # Pop and remove the first 31 items from the list
     $      # Convert each codepoint-integer to a character
      y     # Join the list to a string
       ☻    # Push 16
        /   # Pop and split the string into parts of size 16
         n  # Join the list by newlines
            # (after which the entire stack is output implicitly as result)

05AB1E, 10 bytes

Without builtin:

₃L31+çJ6ä»

Try it online.

With builtin (5 bytes):

žQ6ä»

Try it online.

Explanation:

₃L          # Push a list in the range [1,95]
  31+       # Add 31 to each
     ç      # Convert the codepoint-integers to ASCII characters
      J     # Join the list together to a string
       6ä   # Split it into 6 parts
         »  # Join by newlines
            # (after which the result is output implicitly)

žQ          # Push a string of all ASCII characters
  6ä»       # Same as above

APL (Dyalog Extended), 12 bytes

6 16⍴' '…'~'

Try it online!

' '…'~' inclusive range from space to tilde

6 16⍴ cyclically reshape into the required number of rows and columns

R, 38 bytes

intToUtf8(rbind(matrix(32:126,16),10))

Try it online!

Python 2, 66 bytes

for i in range(6):print bytearray(j+i*16+32for j in range(16-i/5))

Attempt This Online!

Based on @mousetail's solution; submitted as my own answer by their suggestion.

Python 3, 71 bytes

-2 bytes thanks to @pxeger

for i in range(6):print(''.join(chr(j+i*16+32)for j in range(16-i//5)))

Try it online!

Zsh, 26 bytes

printf %s {\ ..~}|fold -16

Attempt This Online!